【CDQ分治+FFT】LGP4566 [CTSC2018]青蕈领主
【題目】
原題地址
有一個長度為 n n n的排列 a a a,給定 l i l_i li?,表示 a i ? l i + 1 … a i a_{i-l_i+1} \dots a_i ai?li?+1?…ai?在排序后公差為 1 1 1,且 l i l_i li?為滿足條件的最小數字,求方案數。 n ≤ 5 × 1 0 4 n\leq 5\times 10^4 n≤5×104
【解題思路】
以下思路來自大佬的博客
神仙題。
很容易證明所有的連續區間只能相離或者相互包含,不可能相交。因此只要存在區間相交或者 L [ n ] ≠ n L[n] \neq n L[n]??=n,就無解。
有解的時候,把每一個點對應的極大連續區間向包含它的最小的極大連續區間連邊,這顯然是一棵樹。
對于一個根,它代表了一段連續區間,而它的每一個兒子都分別代表了一個極大的連續區間,那么把每一個兒子代表的區間縮成一個點,按原相對大小重新編號后不存在除整個區間以外的連續區間。
就相當于求解 1 , 2 , 3 … , m + 1 1,2,3\dots,m+1 1,2,3…,m+1的方案數( m + 1 m+1 m+1表示兒子的個數,即區間長度),記這個值為 f m f_m fm?。
設第 i i i個點的兒子個數是 c n t i cnt_i cnti?,那么答案就是 ∏ i = 1 n f c n t i \prod_{i=1}^n f_{cnt_i} ∏i=1n?fcnti??
現在求 f i f_i fi?,首先給出一個結論 f n = ( n ? 1 ) f n ? 1 + ∑ i = 2 n ? 2 ( i ? 1 ) f i f n ? i f_n=(n-1)f_{n-1}+\sum_{i=2}^{n-2}(i-1)f_if_{n-i} fn?=(n?1)fn?1?+∑i=2n?2?(i?1)fi?fn?i?
這個遞推式可以這樣證明:
我們對于一個滿足條件的排列 a a a,我們令 b a i = i b_{a_i}=i bai??=i
那么 b b b中值 i ~ j i\sim j i~j的下標對應 a i ~ a j a_i\sim a_j ai?~aj?,也就是說 b b b中一段連續區間,就對應了 a a a中一段連續區間,說明 b b b中不存在不經過最大值的連續區間。
那么 a a a和 b b b一一對應,現在轉化為求滿足 b b b性質的方案數。
考慮從 f n f_n fn?轉移到 f n + 1 f_{n+1} fn+1?
- 從一個合法排列轉移到另一個合法排列,假設原排列是 2 ~ n + 1 2\sim n+1 2~n+1的一個排列,那么插入的 1 1 1只要不與 2 2 2相鄰即可,也就是 ( n ? 1 ) f n ? 1 (n-1)f_{n-1} (n?1)fn?1?
- 從一個非法排列轉移到一個合法排列,原排列必須有且僅有一個極大的不包含最大值的連續區間,設長度為 l ( 2 ≤ l ≤ n ? 2 ) l(2\leq l\leq n-2) l(2≤l≤n?2)。
這個不合法的區間要在插入一個數后合法,等價于分成兩部分,左右都不存在連續區間的方案數。也就是一個合法排列去掉最大值后的兩邊,方案數即為fl。再考慮這段區間的值域(也就是 [ x , x + l ? 1 ] [x,x+l-1] [x,x+l?1])范圍, x > 2 x>2 x>2且 x + l ? 1 < n + 1 x+l-1<n+1 x+l?1<n+1,就有 n ? l ? 1 n-l-1 n?l?1個合法值域。其它部分為 f n ? l f_{n-l} fn?l?
于是
f n = ( n ? 1 ) f n ? 1 + ∑ i = 2 n ? 2 ( n ? i ? 1 ) f i f n ? i f_n=(n-1)f_{n-1}+\sum\limits_{i=2}^{n-2}(n-i-1)f_if_{n-i} fn?=(n?1)fn?1?+i=2∑n?2?(n?i?1)fi?fn?i?
= ( n ? 1 ) f n ? 1 + ∑ i = 1 n ? 2 ( i ? 1 ) f i f n ? i =(n-1)f_{n-1}+\sum_{i=1}^{n-2}(i-1)f_if_{n-i} =(n?1)fn?1?+i=1∑n?2?(i?1)fi?fn?i?
可以用分治 + F F T +FFT +FFT得到 f f f數組,用單調棧處理出 c n t cnt cnt數組。
【參考代碼】
#include<bits/stdc++.h> using namespace std;typedef long long ll; const int mod=998244353,g=3,N=166666; int T,n,top,ans,cnt,flag; int f[N],l[N],a[N],b[N],q[N];int read() {int ret=0;char c=getchar();while(!isdigit(c)) c=getchar();while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return ret; }int qpow(int x,int y) {int ret=1;for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) ret=(ll)ret*x%mod;return ret; } void up(int &x,int y){x+=y;if(x>=mod)x-=mod;} int upm(int x){if(x>=mod)x-=mod;return x;}namespace NTT {int L,m;int rev[N],tmp1[N],tmp2[N];void ntt(int *a,int n,int f){for(int i=0;i<n;++i) if(i>rev[i]) swap(a[i],a[rev[i]]);for(int i=1;i<n;i<<=1){int wn=qpow(g,(mod-1)/(i<<1));if(f==-1) wn=qpow(wn,mod-2);for(int j=0;j<n;j+=i<<1){int w=1;for(int k=0;k<i;++k,w=(ll)w*wn%mod){int x=a[j+k],y=(ll)w*a[i+j+k]%mod;a[j+k]=upm(x+y);a[i+j+k]=upm(x-y+mod);}}}int inv=qpow(n,mod-2);if(f==-1) for(int i=0;i<n;++i) a[i]=(ll)a[i]*inv%mod;}void mul(int *a,int *b,int *c,int l1,int l2){for(L=0,m=1;m<=l1+l2;m<<=1) ++L;for(int i=0;i<m;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));for(int i=0;i<l1;++i) tmp1[i]=a[i];for(int i=l1;i<m;++i) tmp1[i]=0;for(int i=0;i<l2;++i) tmp2[i]=b[i];for(int i=l2;i<m;++i) tmp2[i]=0;ntt(tmp1,m,1);ntt(tmp2,m,1);for(int i=0;i<m;++i) c[i]=(ll)tmp1[i]*tmp2[i]%mod;ntt(c,m,-1);} }; using NTT::mul;void cdq(int l,int r) {if(l==r){up(f[l],(ll)f[l-1]*(l-1)%mod);return;}int mid=(l+r)>>1;cdq(l,mid);for(int i=l;i<=mid;++i)a[i-l]=(ll)f[i]*(i-1)%mod,b[i-l]=f[i];mul(a,b,a,mid-l+1,mid-l+1);for(int i=max(l<<1,mid+1);i<=r;++i) up(f[i],a[i-(l<<1)]);if(l^2){for(int i=2;i<=min(l-1,r-l);++i) a[i-2]=f[i];for(int i=l;i<=mid;++i) b[i-l]=f[i];mul(a,b,a,min(l-1,r-l)-1,mid-l+1);for(int i=max(l+2,mid+1);i<=r;++i) up(f[i],(ll)a[i-l-2]*(i-2)%mod);}cdq(mid+1,r); }int main() { #ifndef ONLINE_JUDGEfreopen("LGP4566.in","r",stdin);freopen("LGP4566.out","w",stdout); #endifT=read();n=read();f[0]=1;f[1]=2;cdq(2,n-1);while(T--){ans=1;top=0;for(int i=1;i<=n;++i) l[i]=read();if(l[n]^n){puts("0");continue;}for(int i=1;i<=n;++i){cnt=flag=0;while(top && i-l[i]+1<=q[top]){if(i-l[i]+1>q[top]-l[q[top]]+1){flag=1;break;}--top;++cnt;}if(flag) break;q[++top]=i;ans=(ll)ans*f[cnt]%mod;}if(flag) puts("0"); else printf("%d\n",ans);}return 0; }【總結】
分治 F F T FFT FFT的過程忘了,還是看了別人的代碼才搞清楚的。
巨菜無比。
總結
以上是生活随笔為你收集整理的【CDQ分治+FFT】LGP4566 [CTSC2018]青蕈领主的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS 如何上传TestFlight
- 下一篇: 80后(转载)