zoukankan      html  css  js  c++  java
  • 6830. 【2020.10.25提高组模拟】排列

    一个排列,每次可以将一个字符提到开头或结尾,问进行至多(k)次后能够形成的排列的方案数。

    (k)(0)(n-1)的答案都要计算。

    (nle 1000)


    显然,答案为包含着至少一个长度大于等于(n-k)的子段的排列的方案数。

    现在变成了统计多少个排列,每个上升子段的长度都小于(k)

    以前见过现在不会了。

    有种显然错误的方法:(f_i)表示长度为(i)的合法排列的方案数。转移的时候直接枚举下一段上升子段的长度。很显然由于不知道两个子段是否合在一起变成一个长的上升子段,所以它是错误的。

    现在用容斥系数给修正它。设长度为(i)的子段的容斥系数为(f_i)。考虑对于一个最终的排列,它的其中一个极长的上升子段会被如何贡献到。设(F(x))(f)的生成函数。则有:(sum_{ige 1} F^i(x)=sum _{i=1}^{k-1}x^i)。把(F(x))解出来就好了。

    可以发现(F(x))只有(O(frac{n}{k}))项。所以时间复杂度是有保障的。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 1010
    #define ll long long
    int n,mo;
    ll C[N][N];
    ll f[N],g[N];
    ll work(ll k){
    	memset(g,0,sizeof(ll)*(n+1));
    	g[0]=1;
    	for (int i=1;i<=n;++i){
    		for (int j=0;k*j+1<=i;++j)			
    			(g[i]+=g[i-(k*j+1)]*C[i][k*j+1])%=mo;
    		for (int j=0;k*j+k<=i;++j)
    			(g[i]-=g[i-(k*j+k)]*C[i][k*j+k])%=mo;
    	}
    	return (g[n]+mo)%mo;
    }
    int main(){
    	freopen("in.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    //	freopen("permutation.in","r",stdin);
    //	freopen("permutation.out","w",stdout);
    	scanf("%d%d",&n,&mo);
    	for (int i=0;i<=n;++i){
    		C[i][0]=1;
    		for (int j=1;j<=i;++j)
    			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
    	}
    	ll fac=1;
    	for (int i=1;i<=n;++i)
    		fac=fac*i%mo;
    	for (int k=0;k<n;++k)
    		printf("%lld
    ",(fac-work(n-k)+mo)%mo);
    	return 0;
    }
    
  • 相关阅读:
    考试心得 模拟18
    模拟17 题解
    模拟16 题解
    考试心得 模拟17
    模拟15 题解(waiting)
    BZOJ2653 middle 【主席树】【二分】*
    BZOJ3932 CQOI2015 任务查询系统 【主席树】
    与或 【线段树】 *
    BZOJ1014 JSOI2008 火星人prefix 【非旋转Treap】*
    网络流--最大权闭合子图 *
  • 原文地址:https://www.cnblogs.com/jz-597/p/13893152.html
Copyright © 2011-2022 走看看