UOJ#401. 【CTSC2018】青蕈领主 分治,FFT
原文链接www.cnblogs.com/zhouzhendong/p/UOJ401.html
题解
首先,对于一个排列,它的连续段一定只有包含关系,没有相交关系。
我们可以据此得到一棵表示连续段的树。
对于一个连续段节点,它有若干儿子。
由于它的每一个儿子都是连续段,所以我们可以将这些儿子各自看作一个数。设节点x的度数为 d[x]。
设 f[x] 表示 L 数组为 1,1,1,...1,L+1 这样的排列个数,那么答案就是 $\prod f[d[x]]$ 。
然后我们得到了一个关于 f[x] 的迷之式子:
$$f[i] = (i-1) \cdot f[i-1] + \sum_{j=2}^{i-2} (j-1) \cdot f[j] \cdot f[i-j]$$
然后我们考虑对这个东西做分治FFT来求。
这个分治 FFT 的大致流程如下:
求解区间 [L,R] 时,如果 L = R,那么分治结束。
否则,设 mid = (L+R)/2,先 solve(L,mid) 。
然后考虑计算 [L,mid] 对 [mid+1,R] 的贡献。
这时需要分类讨论:
若 L = 1,那么直接 FFT
若 L > 1,那么需要计算两部分贡献:
1. $j\in [2,R-L],i-j\in[L,mid]$
2. $i-j\in [2,R-L], j\in[L,mid]$
各自FFT即可。
时间复杂度 $O(n\log^2 n)$ 。
代码
#include <bits/stdc++.h> #define clr(x) memset(x,0,sizeof x) #define For(i,a,b) for (int i=a;i<=b;i++) #define Fod(i,b,a) for (int i=b;i>=a;i--) #define fi first #define se second #define pb(x) push_back(x) #define mp(x,y) make_pair(x,y) #define outval(x) printf(#x" = %d\n",x) #define outtag(x) puts("---------------"#x"---------------") #define outarr(a,L,R) printf(#a"[%d..%d] = ",L,R);\ For(_x,L,R)printf("%d ",a[_x]);puts("") using namespace std; typedef long long LL; LL read(){ LL x=0,f=0; char ch=getchar(); while (!isdigit(ch)) f|=ch=='-',ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } const int N=1<<17,mod=998244353; int Pow(int x,int y){ int ans=1; for (;y;y>>=1,x=(LL)x*x%mod) if (y&1) ans=(LL)ans*x%mod; return ans; } void Add(int &x,int y){ if ((x+=y)>=mod) x-=mod; } void Del(int &x,int y){ if ((x-=y)<0) x+=mod; } int Del(int x){ return x<0?x+mod:x; } namespace fft{ int w[N],R[N]; void init(int n){ int d=0; while ((1<<d)<n) d++; For(i,0,n-1) R[i]=(R[i>>1]>>1)|((i&1)<<(d-1)); w[0]=1,w[1]=Pow(3,(mod-1)/n); For(i,2,n-1) w[i]=(LL)w[i-1]*w[1]%mod; } void FFT(int *a,int n,int flag){ if (flag<0) reverse(w+1,w+n); For(i,0,n-1) if (i<R[i]) swap(a[i],a[R[i]]); for (int t=n>>1,d=1;d<n;d<<=1,t>>=1) for (int i=0;i<n;i+=d<<1) for (int j=0;j<d;j++){ int tmp=(LL)w[t*j]*a[i+j+d]%mod; a[i+j+d]=Del(a[i+j]-tmp); Add(a[i+j],tmp); } if (flag<0){ reverse(w+1,w+n); int inv=Pow(n,mod-2); For(i,0,n-1) a[i]=(LL)a[i]*inv%mod; } } } using fft::FFT; namespace pre{ int f[N]; int a[N],b[N]; void solve(int L,int R){ if (L==R){ f[L]=((LL)f[L-1]*(L-1)+f[L])%mod; return; } int mid=(L+R)>>1; solve(L,mid); if (L>1){ int n=1; while (n<(mid-L+1)+(R-L)) n<<=1; fft::init(n); memset(a,0,n<<2),memset(b,0,n<<2); For(i,L,mid) a[i-L]=f[i]; For(i,2,R-L) b[i]=(LL)(i-1)*f[i]%mod; FFT(a,n,1),FFT(b,n,1); For(i,0,n-1) a[i]=(LL)a[i]*b[i]%mod; FFT(a,n,-1); For(i,mid+1,R) Add(f[i],a[i-L]); memset(a,0,n<<2),memset(b,0,n<<2); For(i,L,mid) a[i-L]=(LL)(i-1)*f[i]%mod; For(i,2,R-L) b[i]=f[i]; FFT(a,n,1),FFT(b,n,1); For(i,0,n-1) a[i]=(LL)a[i]*b[i]%mod; FFT(a,n,-1); For(i,mid+1,R) Add(f[i],a[i-L]); } else { int n=1; while (n<mid*2+1) n<<=1; memset(a,0,n<<2),memset(b,0,n<<2); For(i,2,mid) a[i]=f[i]; For(i,2,mid) b[i]=(LL)(i-1)*f[i]%mod; fft::init(n); FFT(a,n,1),FFT(b,n,1); For(i,0,n-1) a[i]=(LL)a[i]*b[i]%mod; FFT(a,n,-1); For(i,mid+1,R) Add(f[i],a[i]); } solve(mid+1,R); } void prework(int n){ f[0]=1,f[1]=2; solve(1,n); // For(i,2,n){ // f[i]=(LL)f[i-1]*(i-1)%mod; // For(j,2,i-2) // Add(f[i],(LL)(j-1)*f[j]%mod*f[i-j]%mod); // } //1 2 2 4 16 88 600 4800 43680 443296 4949920 60217408 792134528 } } int T,n; int a[N]; void Solve(){ For(i,1,n) a[i]=read(); if (a[n]!=n) return (void)(puts("0")); int ans=1; For(i,1,n){ int c=0,j; for (j=i-1;j>i-a[i];j-=a[j]) c++; if (j<i-a[i]) return (void)(puts("0")); ans=(LL)ans*pre::f[c]%mod; } printf("%d\n",ans); } int main(){ pre::prework(50000); T=read(),n=read(); while (T--) Solve(); return 0; }