zoukankan      html  css  js  c++  java
  • 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]);
        }
    }
    
  • 相关阅读:
    js内置数据类型
    vue禁止复制的方式
    阻止element组件中的<el-input/>的粘贴功能
    Vue插件集合
    qs.parse()、qs.stringify()、JSON.stringify() 用法及区别
    es6数组的一些函数方法使用
    文章段落首字母缩进两个字符
    深圳scala-meetup-20180902(3)- Using heterogeneous Monads in for-comprehension with Monad Transformer
    深圳scala-meetup-20180902(2)- Future vs Task and ReaderMonad依赖注入
    深圳scala-meetup-20180902(1)- Monadic 编程风格
  • 原文地址:https://www.cnblogs.com/hznumqf/p/14699038.html
Copyright © 2011-2022 走看看