51nod1472 CF549F 取餘最大值

2021-07-23 03:28:09 字數 1194 閱讀 6394

有乙個長度為n的陣列a,現在要找乙個長度至少為2的子段,求出這一子段的和,然後減去最大值,然後對k取餘結果為0。

問這樣的子段有多少個。

首先有乙個想法,就是建立笛卡爾樹。

那麼對於乙個節點,其作為最大值的區間是它的子樹。

接下來我們考慮如何計算包含i的有多少個合法區間。

對於包含i的[l,r],sum[r]-sum[l-1]-a[i]是k的倍數。

我們全部模k,就會變成sum[r]=sum[l-1]+a[i]或者sum[l-1]=sum[r]-a[i]

考慮列舉乙個l或者r,例如列舉l,然後在右邊區間查詢對應數值的位置有多少個。

區間查詢可以隨便做,因為無修改可以考慮對每個權值建vector,然後用lower_bound和upper_bound。

現在考慮列舉的複雜度,對於i的左右子樹,列舉size較小的一方,那麼複雜度應該和啟發式合併一致,列舉的複雜度是n log n。

#include

#include

#include

#define fo(i,a,b) for(i=a;i<=b;i++)

#define fd(i,a,b) for(i=a;i>=b;i--)

using

namespace

std;

typedef

long

long ll;

const

int maxn=300000+10;

vector

b[1000000+10];

int a[maxn],left[maxn],right[maxn],s[maxn],sum[maxn];

int i,j,k,l,t,n,m,top;

ll ans;

int read()

return x;

}int find(int l,int r,int v)

int main()

fo(i,1,n)

top=0;

s[0]=n+1;

fd(i,n,1)

fo(i,0,n)

b[sum[i]].push_back(i);

fo(i,1,n)

if (i-left[i]1,i)

}else

}printf("%i64d\n",ans-n);

}

51Nod1472 取餘最大值

題目看這裡 又是乙個七級題目 妥妥的分治啊,列舉右端點,左邊分兩段計數就好啦 關於標解 網上好像很多都是字首和 二分之類的,反正應該差不多快吧 include include include define n 300010 define ll long long using namespace st...

51nod1472取餘最大值 分治

description 有乙個長度為n的陣列a,現在要找乙個長度至少為2的子段,求出這一子段的和,然後減去最大值,然後對k取餘結果為0。問這樣的子段有多少個。sample input 4 31 2 3 4 sample output 3考慮分治,對於最大值分情況討論一下即可。include incl...

51nod 1494 選舉拉票 cf458C

中文題面 考慮把所有人的票分別從大到小排序之後可以看做n條線段 列舉自己的票數為i,所以每條線段超過i的部分必須收買,如果還不夠就到前面挑不夠的 104 求前k小想到權值線段樹 應該是類似的東西 include include include define n 100000 define ll lo...