ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-02-28 19:01:25  阅读:221  来源: 互联网

标签:练习赛 ifac int ll 77 牛客 sum mo 极长


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

\(n,m\le 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_{i\ge 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("\n");
}
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\n",ans);
	return 0;
}

标签:练习赛,ifac,int,ll,77,牛客,sum,mo,极长
来源: https://www.cnblogs.com/jz-597/p/14460074.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有