【纪中集训2019.3.25】染色问题

题目

描述

​ 有一个纸片,纸片上有\(n\)个格子,初始时没有颜色;

​ 某个游戏的内容是进行\(m\)次染色,使得染完后\(n\)个格子一定有颜色;

​ 每次可以选择一个区间\([l,r](l \le r)\)去染(不能不染),颜色可以覆盖;

​ 问最后染出的序列有多少种;

范围

\(n,m \le 10^6\) ;

题解

  • 由于可以覆盖这个条件处理起来比较麻烦;

  • 考虑每次是插入一段颜色;

  • \[\begin{cases} &f_{i,j} = f_{i-1,j} + \sum_{k=0}^{j-1} \ f_{i-1,k}(k+1) &(i \lt m)\\ &f_{i,j} = \sum_{k=1}^{j-1} \ f_{i-1,k}(k+1) &(i = m) \\ \end{cases} \]

    • 插播一个我自己的十分没用的想法:

    • 只考虑管第一个转移,设 $ F_{i} $ 为 $ f_{i,j} $ 的 $ OGF $ 对比 $ f_{i,j}和f_{i,j-1} $ ,有:

    • \[F_{i} = F_{i-1} + xF_i + x^2(F_{i-1})' ; \\ F_{i} = \frac{(x^2F_{i-1})'-F_{i-1}}{1-x} ;\\ \]

    • 然后就没有然后了,。。。。。,TAT​;

  • 说正解:

  • 方程中存在两类贡献,一种是\(f_{i,j} \times (j+1)\),一种是\(f_{i,j}\times 1\);

  • 同时要求最后一次转移一定是第二种;

  • 枚举第一种转移的次数\(k\),求得贡献和后乘以\((^{m-1}_{k-1})\)

  • 考虑每次更新之后的形成的第一种转移次数的序列:\(0 \lt a_1 \lt a_2 \ \lt ,\cdots, \lt a_k = n\)

  • 对于一种转移序列的贡献就是:\((a_1+1)\cdots(a_{k-1}+1)\) ;

  • 这其实是\([x^{n-k}]\Pi_{i=1}^{n-1}(x+i+1)\) ;

  • 所以答案是:\(\sum_{k=1}^{m} (^{m-1}_{k-1})[x^{n-k}]\Pi_{i=1}^{n-1}(x+i+1)\);

  • 类似https://www.cnblogs.com/Paul-Guderian/p/10519990.html倍增可以\(nlogn\) ;

    #include<bits/stdc++.h>
    #define ll long long 
    #define mod 998244353
    #define il inline 
    using namespace std;
    const int N=4000010;
    int n,m,fac[N],inv[N],len,L,rev[N],f[N],g[N],a[N],b[N];
    il int pw(int x,int y){
    	int re=1;
    	if(y<0)y+=mod-1; 
    	while(y){
    		if(y&1)re=(ll)re*x%mod;
    		y>>=1;x=(ll)x*x%mod;
    	}
    	return  re;
    }
    il void inc(int&x,int y){x+=y;if(x>=mod)x-=mod;}
    const int G=3;
    il void ntt(int*A,int F){
    	for(int i=0;i<len;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
    	for(int i=1;i<len;i<<=1){
    		int wn=pw(G,F*(mod-1)/(i<<1));
    		for(int j=0;j<len;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[j+k+i]%mod;
    				A[j+k]=(x+y)%mod,A[j+k+i]=(x-y+mod)%mod;
    			}
    		}
    	}
    	if(!~F){
    		int iv=pw(len,mod-2);
    		for(int i=0;i<len;++i)A[i]=(ll)A[i]*iv%mod;
    	}
    }
    void solve(int n){
    	if(n==1){f[0]=2;f[1]=1;return;}
    	if(n&1){
    		solve(n-1);
    		for(int i=n-1;~i;--i)inc(f[i+1],f[i]),f[i]=(ll)(n+1)*f[i]%mod;
    		return ;
    	}
    	solve(n>>=1);
    	for(len=1,L=0;len<(n+1)<<1;L++,len<<=1);
    	for(int i=0;i<=len;++i){
    		rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
    		a[i]=b[i]=g[i]=0;
    	}
    	for(int i=0,pwn=1;i<=n;++i,pwn=(ll)pwn*n%mod){
    		a[i]=(ll)f[n-i]*fac[n-i]%mod;
    		b[i]=(ll)pwn*inv[i]%mod;
    	}
    	ntt(a,1);ntt(b,1);
    	for(int i=0;i<len;++i)a[i]=(ll)a[i]*b[i]%mod;
    	ntt(a,-1);
    	for(int i=0;i<=n;++i)g[i]=(ll)inv[i]*a[n-i]%mod;
    	ntt(f,1);ntt(g,1);
    	for(int i=0;i<len;++i)f[i]=(ll)f[i]*g[i]%mod;
    	ntt(f,-1);
    }
    int C(int x,int y){return (ll)fac[x]*inv[y]%mod*inv[x-y]%mod;}
    int main(){
    	freopen("color.in","r",stdin);
    	freopen("color.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for(int i=fac[0]=inv[0]=1;i<=m||i<=n;++i){
    		fac[i]=(ll)fac[i-1]*i%mod;
    		inv[i]=pw(fac[i],mod-2);
    	}
    	solve(n-1);
    	int ans=0;
    	for(int i=1;i<=n&&i<=m;++i){
    		int tmp=(ll)f[n-i]*C(m-1,i-1)%mod;
    		inc(ans,tmp);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
posted @ 2019-04-01 19:45  大米饼  阅读(314)  评论(0编辑  收藏  举报