• 牛客练习赛77 F 小G的排列


    求有多少个长度为(n)的排列,满足最长的相邻差不超过(1)的子区间长度小于等于(m)

    (n,mle 5000)


    老套路容斥。

    先用个DP计算出所有的方案:一个方案可以看成,把一个(1)(n)的顺序排列划分成(i)段,然后将这(i)段进行排列,并且其中长度大于等于(2)的段反转。于是设(g_{i,j}),有(g_{i,j}=sum g_{k,j-1}f_{i-k}2^{[i-k>1]})。其中(f)是容斥系数。

    接下来考虑一个特定的方案,我们希望它的极长段小于等于(m),否则没有共线。一个固定的极长段被算到的方案的生成函数可以表示为(sum_{ige 1} F^i),它等于(sum_{i=1}^m x^i)。解方程即可得到(F)

    直接做是(O(n^3))。解出(F)之后发现(F)有些规律,按照这个规律前缀和可以搞到(O(n^2))


    然而可能有另一种想法:干嘛不把(2)丢进容斥系数中?

    我对此进行了尝试并猜想了若干个方程,有些不好解我甚至搬出了拉格朗日反演。然而每次解出来的系数都是错的。

    我想到了一个解释:当转移一段的时候没有如果没有固定它的方向,对于某种特定的方案,它没有定向,于是极长段还是不确定的。所以上面的方法就出锅了。


    没有写(O(n^2))的做法。下面代码中有大量的用于尝试的代码。

    using namespace std;
    #include <bits/stdc++.h>
    #define N 5005
    #define ll long long
    #define mo 1000000007
    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;
    }
    ll fac[N],ifac[N],inv[N];
    void initC(int n){
    	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;
    	inv[1]=1;
    	for (int i=2;i<=n;++i)
    		inv[i]=(mo-mo/i)*inv[mo%i]%mo;
    }
    int n,m;
    int f[N],g[N][N];
    void getdiv(int c[],int a[],int b[]){
    	static int t[N];
    	memset(t,0,sizeof(int)*(n+1));
    	for (int i=0;i<=n;++i){
    		t[i]=(a[i]-t[i]+mo)%mo;
    		for (int j=1;i+j<=n;++j)
    			t[i+j]=(t[i+j]+(ll)t[i]*b[j])%mo;
    	}
    	memcpy(c,t,sizeof(int)*(n+1));
    }
    void multi(int c[],int a[],int b[]){
    	static int t[N];
    	memset(t,0,sizeof(int)*(n+1));
    	for (int i=0;i<=n;++i)
    		for (int j=0;j<=i;++j)
    			t[i]=(t[i]+(ll)a[j]*b[i-j])%mo;
    	memcpy(c,t,sizeof(int)*(n+1));	
    }
    void getf(){
    	static int a[N],b[N],c[N],d[N],e[N];
    	a[1]=1,a[m+1]=-1;
    	b[0]=1,b[m+1]=-1;
    	getdiv(f,a,b);
    	for (int i=2;i<=n;++i)
    		f[i]=f[i]*2%mo;
    //	a[0]=1;
    //	b[0]=1;
    //	for (int i=2;i<=n;++i)
    //		b[i-1]=1;
    //	getdiv(c,a,b);
    //	memcpy(d,c,sizeof d);
    //	for (int k=1;k<=n;++k){
    //		e[k]=d[k-1]*inv[k]%mo;
    //		multi(d,d,c);
    //	}
    //	a[0]=0,a[1]=1;
    //	for (int i=2;i<=m;++i)
    //		a[i]=2;
    //	for (int j=0;j<=n;++j)
    //		f[j]=(f[j]+(ll)e[n]*a[j])%mo;
    //	for (int i=n;i>=1;--i){
    //		multi(f,f,a);
    //		for (int j=0;j<=n;++j)
    //			f[j]=(f[j]+(ll)e[i]*a[j])%mo;
    //	}
    	for (int i=0;i<=n;++i)
    		printf("%d ",f[i]);
    	printf("
    ");
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	scanf("%d%d",&n,&m);
    	initC(n+1);
    	getf();
    	g[0][0]=1;
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=i;++j)
    			for (int k=0;k<i;++k)
    				g[i][j]=(g[i][j]+(ll)g[k][j-1]*f[i-k]%mo)%mo;
    	ll ans=0;
    	for (int i=1;i<=n;++i)
    		(ans+=g[n][i]*fac[i])%=mo;
    //	ans%=mo;
    	ans=(ans+mo)%mo;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    一行语句让你的浏览器变成记事本
    为啥只有IPv4和IPv6,没有IPv5呢?
    在线C++编译器
    怎么让Visual Studiot在遇到一个project编译错误时停止编译其它project
    C#的继承类中static constructor的调用问题
    C#中静态构造函数导致的一个deadlock
    一个C++的轻量级的logger实现
    一个免费轻量的Mathematica替代Mathics
    实时交通路况
    构建WAP邮件发送服务器
  • 原文地址:https://www.cnblogs.com/jz-597/p/14460074.html
Copyright © 2020-2023  润新知