zoukankan      html  css  js  c++  java
  • 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.  $jin [2,R-L],i-jin[L,mid]$

      2.  $i-jin [2,R-L], jin[L,mid]$

    各自FFT即可。

    时间复杂度 $O(nlog^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
    ",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
    ",ans);
    }
    int main(){
    	pre::prework(50000);
    	T=read(),n=read();
    	while (T--)
    		Solve();
    	return 0;
    }
    

      

  • 相关阅读:
    sabaki and leelazero
    apply current folder view to all folders
    string operation in powershell
    wirte function in powershell
    add environment path to powershell
    Module in powershell
    sql prompt
    vmware中鼠标在部分区域不能使用
    调整多个控件的dock的顺序
    行为型模型 策略模式
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ401.html
Copyright © 2011-2022 走看看