題目連接git
給定長爲\(n\)的序列\(A_i\)和一個整數\(K\)。把它劃分紅若干段,知足每段中剛好出現過一次的數的個數\(\leq K\)。求方案數。
\(K\leq n\leq10^5\)。數據結構
設\(f[i]\)表示前\(i\)個數的答案,\(g[j]\)表示\(j\sim i\)剛好出現過一次的數的個數。有\[f[i]=\sum_{j\leq i,\ g[j]\leq K}f[j-1]\]優化
記\(las_i\)爲\(A_i\)上次出現的位置下標。每次\(i\)移動時,\(g[j]\)的變化就是,\([las_i+1,i]\)區間\(+1\),\([las_{las_i}+1,las_i]\)區間\(-1\)。
也就是要動態修改\(g[j]\),求\(g[j]\leq K\)的\(f[j-1]\)的和。spa
數據結構什麼的很差搞。考慮直接分塊。
一種最簡單的想法是,塊內sort
後維護前綴和,查詢的時候二分。複雜度\(O(n\sqrt n\log(\sqrt n))\)(注意確實是\(\log(\sqrt n)\)),但過不去。code
設\(s[i][j]\)表示第\(i\)塊中,\(g[k]\leq j\)的\(f[k]\)的和,\(tag[i]\)表示第\(i\)塊的總體修改標記。
考慮區間修改。對於整塊直接打標記。對於零散部分,由於只是\(+1\),容易發現對於第\(i\)塊,只有\(s[i][g[j]]\)的值改變了,且只是少掉了\(f[j-1]\)(\(j\)是影響到的下標,顯然能夠暴力枚舉)。那麼能夠暴力更新\(s[i]\)。
對於整塊的查詢,假設是第\(i\)塊,須要知足\(j+tag[i]\leq k\),即\(j\leq k-tag[i]\),那麼\(s[i][k-tag[i]]\)就是答案了。
那麼這樣就能夠啦。ip
其實還能夠優化。
把每一個修改拆成前綴修改,即:\([1,i]\)區間\(+1\),\([1,las_i]\)區間\(-2\),\([1,las_{las_i}]\)區間\(+1\)。
這樣有什麼好處呢。設\(i\)所在的塊爲\(p\)。那麼對\(p\)塊零散部分暴力修改,對\(1\sim p-1\)塊統一打上標記\(tag\)。
能夠發現這樣\(s[i][j]\)的第二維是\(O(\sqrt n)\)級別的(只有同塊內的會影響它,其它值都打到\(tag\)上了)!也就是空間只須要\(O(n)\)就夠了。
並且若是咱們把詢問也拆成前綴的形式(其實原本就是前綴),那\(tag\)徹底不須要直接打到\(1\sim p-1\)上,只須要在\(p\)上打就能夠了。查詢的時候維護一個\(tag\)的後綴和便可。
這樣設塊大小爲\(B\),修改複雜度就只有\(O(B)\),查詢複雜度仍是\(O(B+\frac nB)\),可是某些修改比較多查詢比較少的題目就能夠調整\(B\)的大小解決啦。
雖然在這題複雜度依舊是\(O(n\sqrt n)\),可是常數不知道優秀到了哪裏去。get
中間過程(包括查詢啊=-=)\(s\)的第二維多是負的,但絕對值在\(\sqrt n\)內。it
//233ms 2600KB #include <cstdio> #include <cctype> #include <algorithm> #define B 150 #define mod 998244353 #define Mod(x) x>=mod&&(x-=mod) #define Add(x,v) (x+=v)>=mod&&(x-=mod) #define gc() getchar() typedef long long LL; const int N=1e5+5,M=N/B+3; int bel[N],f[N],g[N],tag[M],s[M][B+3<<1]; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-48,c=gc()); return now; } void Update(int p,int v) { int *s=::s[bel[p]]; for(int i=B; i<=B<<1; ++i) Add(s[i],v); } void Modify(int p,int v) { int bel=::bel[p],*s=::s[bel]; tag[bel]+=v; for(int i=bel*B+1; i<=p; ++i) { if(v==1) Add(s[g[i]+B],mod-f[i-1]); else Add(s[g[i]-1+B],f[i-1]), Add(s[g[i]-2+B],f[i-1]); g[i]+=v; } } int Query(int p,int K) { int bel=::bel[p],sum=tag[bel]; LL res=0; for(int i=bel*B+1; i<=p; ++i) g[i]<=K&&(res+=f[i-1]); while(bel--) { // assert(sum>=0); // if(sum<=K) res+=s[bel][std::min(B<<1,K-sum+B)];//WA:sum may be >K if(std::abs(sum-K)<=B) res+=s[bel][K-sum+B]; else if(sum<K) res+=s[bel][B<<1]; sum+=tag[bel]; } return res%mod; } int main() { static int las[N],pre[N]; int n=read(),K=read(); for(int i=1; i<=n; ++i) bel[i]=(i-1)/B; f[0]=1; for(int i=1; i<=n; ++i) { int a=read(); las[i]=pre[a], pre[a]=i; Update(i,f[i-1]), Modify(i,1); if(las[i]) { Modify(las[i],-2); if(las[las[i]]) Modify(las[las[i]],1); } f[i]=Query(i,K); } printf("%d\n",f[n]); return 0; }