Codeforces Round #717 (Div. 2) E. Baby Ehab Plays with Permutations 第一类斯特林数,容斥,DP

Codeforces Round #717 (Div. 2) E. Baby Ehab Plays with Permutations 第一类斯特林数,容斥,DP

题意

给定一个长度为(n)的排列(初始就是1到n),每次操作可以交换两个数,(k)次操作后能得到多少种排列

[2 leq n leq10^9\ 1 leq k leq 200 ]

答案取余(1e9 + 7)

分析

(Sol1):

先给一个相对来说比较intuitive的做法,奈何对这块理解不深。注意到答案就是第一类斯特林数的后(k)项的偶数项的和,由于(k)较小(n)较大,并且有性质(S_1(n,n-k))是关于(n)(2k+1)次多项式

于是对(S_1)进行若干次拉格朗日插值即可

(Sol2:)

以下是官方题解做法。

我们可以认为对排列做(k)次操作计数相当于这样一个问题:

有多少种排列经过(k)次操作可以使其有序。用类似解决错排的思想,考虑新加入一个数

(dp[n][j])表示前(n)个数恰好经过(j)次有序的贪心方案数(每次都去交换到正确位置),那么第(n)个数是(n),可以不用操作,否则和前面的(n)交换,这里(n)(n - 1)种可能

因此(dp[n][j] = dp[n - 1][j] + (n - 1)dp[n - 1][j - 1])

注意到这是反着做且贪心的情况,事实上根本不需要每次都交换到正确位置,比如可以对同样一对位置交换两次,这相当于将一个合并的环又拆开

容易发现我们要求的实际上是(dp[n][j] + dp[n][j - 2] + dp[n][j - 4]...)

然而(n)太大了,我们需要从(k)的角度出发考虑此题。

容易发现最终最多影响(2 k)个位置,这给了我们发挥空间

答案是否是 (sum_{j=0}^{k}sum_{i=0}^j binom{n}{j} dp[j][i]) 这么做显然会带来重复计算

(newdp[n][j])表示前(n)个数交换(j)次恰好有序,且没有一个数在自己原本的位置的方案数,

其容斥思想完全可以仿照错排问题的容斥做法

回顾一下错排问题

可以转化为求并集(满足存在(P_i = i)的排列的数量 )

套用容斥的公式展开就变成了求(k)个数的交集的大小,这(k)个数满足(P_i = i),那么剩下的数可以随意排列

因此有

[|igcup_{i=1}^n S_i| = sum_{k=1}^n(-1)^{k-1} sum_{a_1...k}|igcap_{i=1}^kS_{a_i}|\ = sum_{k=1}^n(-1)^{k-1} binom{n}{k} (n-k)!\ = sum_{k=1}^n (-1)^{k-1} frac{n!}{k!}\ = n! sum_{k=1}^n frac{(-1)^{k-1}}{k!} ]

到了此题,计算(newdp[n][j])可以先转化为补集(从(dp[n][j])中去减),于是变为了求满足有在原来的位置的并集

套用容斥就变为了求(k)个位置的交集,这(k)个位置且其他随意排列的方案其实就是(dp[n-k][j])

于是

[newdp[n][j] = sum_{f=0}^n (-1)^f binom{n}{f} dp[n-f][j] ]

与是对答案(newdp)乘个组合数然后累加偶数次前缀和即可

code1

#include <bits/stdc++.h>

const int mod = 1e9 + 7;

using ll = long long;
int inv(int x) { return x == 1 ? 1 : (ll) ( mod - mod / x ) * inv(mod % x) % mod; }

int x[1005],y[1005],f[1005],n,k,S1[1005][1005];

int lagrange(int n,int k) {
	int ans = 0;
	for (int i = 1; i <= n; ++i) {
		//printf("<%d %d>
",x[i],y[i]);
		ll sum=1, sum2=1;
		for (int j = 1; j <= n; ++j) {
			if (j == i) continue;
			sum = sum*(k - x[j] + mod) % mod;
			sum2 = sum2 * (x[i] - x[j] + mod) % mod;
		} ans = (ans + y[i] * sum % mod * inv(sum2) % mod) % mod;
	} //printf("%d
",ans);
	return ans;
}

int calc(int n,int r) {
	int N = 0;
	for (int i = r; i <= r + 2 * k + 3; ++ i) {
		y[++N] = S1[i][i - r];
		x[N] = i;
	} 
	return lagrange(N,n);
} 

int main() {
	scanf("%d%d",&n,&k);
	S1[0][0] = 1;
	for (int i = 1; i <= 1000; ++ i) 
		for (int j = 1; j <= i; ++ j) 
			S1[i][j] = ( (ll) (i - 1) * S1[i-1][j] % mod + S1[i-1][j-1] ) % mod;
	for (int i = 0; i <= k; ++ i) {
		f[i] = calc(n,i);
	//	printf("%d ",f[i]);
	}
	for (int i = 1; i <= k; ++ i) {
		int ans = 0;
		for (int j = i; j >= 0; j -= 2) ans = (ans + f[j]) % mod;
		printf("%d ",ans);
	} 
	return 0;
}

code2

#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
long long ncr[405][405],inv[405],dp[405][405];
long long big_ncr(int n,int r)
{
    long long ret=1;
    for (int i=n-r+1;i<=n;i++)
    ret=(ret*i)%mod;
    for (int i=1;i<=r;i++)
    ret=(ret*inv[i])%mod;
    return ret;
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    ncr[0][0]=1;
    for (int i=1;i<=2*k;i++)
    {
        dp[i][0]=1;
        ncr[i][0]=1;
        for (int j=1;j<=2*k;j++)
        {
            dp[i][j]=(dp[i-1][j]+(i-1)*dp[i-1][j-1])%mod;
            ncr[i][j]=(ncr[i-1][j]+ncr[i-1][j-1])%mod;
        }
    }
    inv[1]=1;
    for (int i=2;i<=2*k;i++)
    inv[i]=inv[mod%i]*(mod-mod/i)%mod;
    int ans[]={1,0};
    for (int j=1;j<=k;j++)
    {
        for (int i=1;i<=min(n,2*j);i++)
        {
            int cnt=0;
            for (int f=0;f<=i;f++)
            cnt=(cnt+(f%2? -1:1)*ncr[i][f]*dp[i-f][j]%mod+mod)%mod;
            ans[j%2]=(ans[j%2]+big_ncr(n,i)*cnt)%mod;
        }
        printf("%d ",ans[j%2]);
    }
}
原文地址:https://www.cnblogs.com/hznumqf/p/14699038.html