• AGC 005D.~K Perm Counting(容斥 DP 二分图)


    题目链接

    (Description)

    给定(n,k),求 满足对于所有(i)(|a_i-i| eq k)的排列的个数。
    (2leq nleq 2000,quad 1leq kleq n-1)

    (Solution)

    容斥。则(Ans=sum_{i=0}^n(-1)^ig(i)(n-i)!),其中(g(i))为至少有(i)个位置满足(|a_i-i|=k)的排列数。

    考虑如何计算(g(x))。每个(i)(i+k)(i-k)连边,可以得到一张二分图,(g(x))就是在这张二分图上选(x)个匹配的方案数。

    我们还可以发现,图中的匹配形成了(2k)条互不相交的链,且每条链上的数模(k)相同(也就是模(k)不同的数是互不影响的,所以枚举模数就可以得到所有链了)。

    如果只有一条长(l)的链,那么就是对(l-1)个点DP,(f[i][j][0/1])表示当前第(i)个点,已经选了(j)个匹配,这个点选不选(这个点选要求上一个点没选)。
    对于(2k)条链也是一样的,只要在链之间加一个点,并强制它不能选((f[i][j][1]=0)),就可以把这些链合在一起DP啦。

    这样复杂度(O(n^2)),然而好多dalao用NTT(nlog n)过掉了orz。


    因为想跑快点(个人习惯)写的有点乱,代码也可以看他的:http://www.cnblogs.com/wxjor/p/9476998.html。

    //20ms	256KB
    #include <cstdio>
    #include <algorithm>
    #define mod 924844033
    typedef long long LL;
    const int N=4005;//2n
    
    int ban[N],f[2][N][2],fac[N];
    
    int main()
    {
    	int n,K,cnt=0; scanf("%d%d",&n,&K);
    	for(int i=1; i<=K; ++i)
    	{
    		ban[++cnt]=1;
    		for(int j=i+K; j<=n; j+=K) ban[++cnt]=0;
    		ban[++cnt]=1;//就是两条链啊 
    		for(int j=i+K; j<=n; j+=K) ban[++cnt]=0;
    	}
    	int p=0; f[p][0][0]=1;
    	for(int i=0; i<cnt; ++i,p^=1)
    		for(int j=0; j<=i; ++j)
    		{
    			f[p^1][j][0]=(f[p][j][0]+f[p][j][1])%mod;
    			if(!ban[i+1]) f[p^1][j+1][1]=f[p][j][0];
    			f[p][j][0]=f[p][j][1]=0;
    		}
    	fac[0]=1;
    	for(int i=1; i<=n; ++i) fac[i]=1ll*fac[i-1]*i%mod;
    	LL ans=0;
    	for(int i=0; i<=n; ++i)
    		ans+=i&1?mod-1ll*fac[n-i]*(f[p][i][0]+f[p][i][1])%mod:1ll*fac[n-i]*(f[p][i][0]+f[p][i][1])%mod;
    	printf("%lld
    ",ans%mod);
    
    	return 0;
    }
    
  • 相关阅读:
    Educational Codeforces Round 49 (Rated for Div. 2)
    Codeforces Round #506 (Div. 3)
    multiset
    C++中substr函数的用法
    7.30 背包问题
    7.29 dp动态规划
    7.27 图论 存图 前向星 最短路 dijstra算法 SPFA算法
    7.26 搜索进阶(状压搜索,迭代加深搜索)
    7.23 深搜广搜
    7.24 二分搜索
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10095512.html
Copyright © 2020-2023  润新知