• 6861. 【2020.11.14提高组模拟】终末作战


    (n)的排列,满足不超过(k+1)段极长的连续段组成,这里的连续段定义为相邻两个数的差值绝对值不超过(1)

    (nle 2*10^5)


    设恰好(k)段组成的方案数为(f_k),至多(k)段组成的方案数为(g_k)

    显然有(g_k=[x^{n}](frac{2x}{1-x}-x)^kk!)。化下式子得(g_{k}=k!sum_{i=0}^k2^i(-1)^{k-i}inom{k}{i}inom{n-k+i-1}{i-1})。可以卷。

    (用另一种方法可能推出(g_k=g!sum_{i=0}^kinom{k}{i}inom{n-i-1}{k-1}),但是它似乎卷不了……)

    为了方便将(f)(g)的下标左移。

    列出式子(g_k=sum f_{i}inom{n-1-i}{k-i}),发现它不是普通的二项式反演,魔改成(g_k(n-1-k)!k!=sum (f_i(n-1-i)!i!)inom{k}{i})发现可以反演了。

    卷一下即可。

    其实这里最恶心的地方,在于有些直接卷的时候可能有些地方会出现((-1)!),于是需要特殊处理一下。。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 524288
    #define mo 998244353
    #define ll long long
    ll qpow(ll x,ll y=mo-2){
    	ll r=1;
    	for (;y;y>>=1,x=x*x%mo)
    		if (y&1)
    			r=r*x%mo;
    	return r;
    }
    int nN;
    int re[N];
    void setlen(int n){
    	int bit=0;
    	for (nN=1;nN<=n;nN<<=1,++bit);
    	for (int i=1;i<nN;++i)
    		re[i]=re[i>>1]>>1|(i&1)<<bit-1;
    }
    void dft(int A[],int flag){
    	for (int i=0;i<nN;++i)
    		if (i<re[i])
    			swap(A[i],A[re[i]]);
    	static int wnk[N];
    	for (int i=1;i<nN;i<<=1){
    		ll wn=qpow(3,flag==1?(mo-1)/(2*i):mo-1-(mo-1)/(2*i));
    		wnk[0]=1;
    		for (int k=1;k<i;++k)
    			wnk[k]=wnk[k-1]*wn%mo;
    		for (int j=0;j<nN;j+=i<<1)
    			for (int k=0;k<i;++k){
    				ll x=A[j+k],y=(ll)A[j+k+i]*wnk[k];
    				A[j+k]=(x+y)%mo;
    				A[j+k+i]=(x-y)%mo;
    			}
    	}
    	for (int i=0;i<nN;++i)
    		A[i]=(A[i]+mo)%mo;
    	if (flag==-1){
    		ll invn=qpow(nN);
    		for (int i=0;i<nN;++i)
    			A[i]=A[i]*invn%mo;
    	}
    }
    void multi(int c[],int a[],int b[],int n){
    	static int A[N],B[N];
    	setlen(n*2);
    	memset(A,0,sizeof(int)*(nN));
    	memset(B,0,sizeof(int)*(nN));
    	for (int i=0;i<=n;++i)
    		A[i]=a[i],B[i]=b[i];
    	dft(A,1),dft(B,1);
    	for (int i=0;i<nN;++i)
    		A[i]=(ll)A[i]*B[i]%mo;
    	dft(A,-1);
    	for (int i=0;i<=n*2;++i)
    		c[i]=A[i];
    }
    ll pw2[N],fac[N],ifac[N];
    void init(int n){
    	pw2[0]=1;
    	for (int i=1;i<=n;++i)
    		pw2[i]=pw2[i-1]*2%mo;
    	fac[0]=1;
    	for (int i=1;i<=n;++i)
    		fac[i]=fac[i-1]*i%mo;
    	ifac[n]=qpow(fac[n]);
    	for (int i=n-1;i>=0;--i)
    		ifac[i]=ifac[i+1]*(i+1)%mo;
    }
    int n,k;
    int a[N],b[N],f[N],g[N],F[N],G[N];
    int main(){
    	freopen("fight.in","r",stdin);
    	freopen("fight.out","w",stdout);
    	scanf("%d%d",&n,&k);	
    	init(n);
    	for (int i=0;i<=n;++i){
    		if (i>0) a[i]=pw2[i]*ifac[i]%mo*ifac[i-1]%mo;
    		if (i<n) b[i]=(i&1?mo-1:1)*ifac[i]%mo*fac[n-i-1]%mo;
    	}
    	multi(g,a,b,n);
    	for (int i=1;i<=n;++i)
    		g[i]=g[i]*fac[i]%mo*fac[i]%mo*ifac[n-i]%mo;
    	g[n]=(g[n]+(n&1?mo-1:1)*fac[n]%mo)%mo;
    	for (int i=0;i<n;++i)
    		G[i]=g[i+1]*fac[n-1-i]%mo*fac[i]%mo;
    	for (int i=0;i<=n;++i){
    		a[i]=G[i]*ifac[i]%mo;
    		b[i]=(i&1?mo-1:1)*ifac[i]%mo;
    	}
    	multi(F,a,b,n);
    	for (int i=0;i<n;++i)
    		F[i]=F[i]*fac[i]%mo;
    	for (int i=0;i<n;++i)
    		f[i]=F[i]*ifac[n-1-i]%mo*ifac[i]%mo;
    	ll ans=0;
    	for (int i=0;i<=k;++i)
    		ans+=f[i];
    	ans%=mo;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Java序列化与反序列化
    Java中的反射机制
    Java中读文件操作
    Java中写文件操作
    判断单链表是否有环
    Java继承与组合
    Java类初始化
    堆排序
    希尔排序
    插入排序
  • 原文地址:https://www.cnblogs.com/jz-597/p/13975958.html
Copyright © 2020-2023  润新知