zoukankan      html  css  js  c++  java
  • 【agc005d】~K Perm Counting

    题目大意

    求有多少中1~n的排列,使得(abs(第i个位置的值-i)!=k)

    解题思路

    考虑容斥,(ans=sum_{i=0}^{n}(-1)^ig[i](n-i)!(g[i]表示至少有i个位置是不合法的方案数))
    考虑如何求g[i]
    将每个位置和每个值都作为一个点,有2n个点,如果第i位置不可以填j,将位置i向值j连边。
    这样,就得到了一个二分图,问题就变成了选i条边的方案数。
    将二分图的每条链拉出来,并在一起,就形成2n个点排成一排,一些相邻点之间有边。
    (f[i][j][0/1])表示,做到第i个点,选了j条边,这个点与上个一点的边是否有选(如果没边就为0)的方案数。
    那么(g[i]=f[2n][i]][0]+f[2n][i][1])

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <map>
    #include <bitset>
    #include <set>
    const int inf=2147483647;
    const int mo=924844033;	
    const int N=4005;
    using namespace std;
    int n,m,tot;
    bool lk[N];
    long long f[N][N][2],jc[N],ans;
    int main()
    {
    	scanf("%d%d",&n,&m);
    	jc[0]=1;
    	for(int i=1;i<=n;i++) jc[i]=1ll*jc[i-1]*i%mo;
    	for(int i=1;i<=m;i++)
    		for(int t=2;t--;)
    			for(int j=i;j<=n;j+=m)
    			{
    				tot++;
    				if(j!=i) lk[tot]=1;
    			}
    	f[0][0][0]=1;
    	for(int i=0;i<n*2;i++)
    		for(int j=0;j<=n;j++)
    		{
    			f[i+1][j][0]=(f[i][j][0]+f[i][j][1])%mo;
    			if(lk[i+1]) f[i+1][j+1][1]=f[i][j][0];
    		}
    	for(int i=0,t=1;i<=n;i++,t=-t)
    	{
    		f[2*n][i][0]=1ll*(f[2*n][i][0]+f[2*n][i][1])*jc[n-i]%mo;
    		ans=(ans+f[2*n][i][0]*t+mo)%mo;
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    win7下的vxworks总结
    ubuntu 无法获得锁 /var/lib/dpkg/lock
    项目中用到了的一些批处理文件
    win7下安装 WINDRIVER.TORNADO.V2.2.FOR.ARM
    使用opencv统计视频库的总时长
    January 05th, 2018 Week 01st Friday
    January 04th, 2018 Week 01st Thursday
    January 03rd, 2018 Week 01st Wednesday
    January 02nd, 2018 Week 01st Tuesday
    January 01st, 2018 Week 01st Monday
  • 原文地址:https://www.cnblogs.com/chen1352/p/9099522.html
Copyright © 2011-2022 走看看