5.15 省选模拟赛 容斥 生成函数 dp

LINK:5.15 T2

avatar
avatar
avatar

个人感觉生成函数更无脑 容斥也好推的样子.

容易想到每次放数和数字的集合无关 所以得到一个dp f[i][j]表示前i个数字 逆序对为j的方案数.

容易得到转移 使用前缀和优化即可。

进一步的可以设出其生成函数 对于第i次放数字 生成函数为(F(x)=1+x^1+x^2+...x^{n-i})

那么容易得到答案的生成函数为 (G(x)=frac{Pi_{i=1}^{n}(1-x^i)}{(1-x)^n})

化简一下 然后dp出来方案数即可 可以发现这个dp是(ksqrt n)

当然也可以容斥 可以发现 其实每个数字都有范围[0,i-1]

我们想要求出 (g_1+g_2+...g_n=k)这个等式的解的个数。

此时隔板法可以求出 方程的解 不过不一定满足 范围。

考虑 容斥 总方案-一个不合法+两个不合法-三个不合法...

容易想到 第i个数字不合法当且仅当其值>=i时不合法 那么利用代表元 就很容易统计其不合法方案.

这样问题变成了 求出 f[i][j]表示i个数字和为j的方案数.

显然这i个数字每个都不相同 那么第一维是一个根号的状态.

所以 转移也很简单 不过值得注意的是需要减掉某个数字>n的方案.

这个在第一次越过的时候减掉即可。

两种方案 殊途同归 写法一模一样.

const ll MAXN=100010;
ll n,k,maxx;
ll f[600][MAXN];
ll fac[MAXN<<1],inv[MAXN<<1];
inline ll ksm(ll b,ll p)
{
	ll cnt=1;
	while(p)
	{
		if(p&1)cnt=cnt*b%mod;
		b=b*b%mod;p=p>>1;
	}
	return cnt;
}
inline void prepare()
{
	fac[0]=1;
	rep(1,maxx,i)fac[i]=fac[i-1]*i%mod;
	inv[maxx]=ksm(fac[maxx],mod-2);
	fep(maxx-1,0,i)inv[i]=inv[i+1]*(i+1)%mod;
}
inline ll C(ll a,ll b){return a<b?0:fac[a]*inv[b]%mod*inv[a-b]%mod;}
signed main()
{
	freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
	get(n);get(k);
	maxx=n+k;prepare();
	ll ww=(ll)sqrt(k*2*1.0)+1;
	f[0][0]=1;
	rep(1,ww,i)
	{
		rep(1,k,j)
		{
			if(j>=i)f[i][j]=(f[i][j-i]+f[i-1][j-i])%mod;
			if(j>n)f[i][j]=(f[i][j]-f[i-1][j-n-1])%mod;
		}
	}
	ll ans=0;
	rep(0,ww,i)
	{
		rep(0,k,j)
		{
			if(i&1)ans=(ans-f[i][j]*C(k-j+n-1,n-1))%mod;
			else ans=(ans+f[i][j]*C(k-j+n-1,n-1))%mod;
		}
	}
	putl(M(ans));
	return 0;
}
原文地址:https://www.cnblogs.com/chdy/p/12905453.html