• 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;
    }
    

      

  • 相关阅读:
    通讯技术
    (1)sqlserver2017安装
    c# api身份验证和授权
    ()centos7 安装python36
    python 包管理
    ()centos7 安装mysql8.0
    [bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治
    bzoj 3544 [ONTAK2010]Creative Accounting 贪心
    BZOJ4300 绝世好题 dp
    bzoj 4295 [PA2015]Hazard 贪心,暴力
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ401.html
Copyright © 2020-2023  润新知