zoukankan      html  css  js  c++  java
  • 【AGC005D】~K Perm Counting(容斥,二分图,计数dp)

    首先正面做不太好做,考虑容斥。

    f ( m ) f(m) f(m) 表示排列中至少有 m m m ∣ P i − i ∣ = k |P_i-i|=k Pii=k 的方案数。

    那么答案就是 ∑ i = 0 n ( − 1 ) i f ( i ) sumlimits_{i=0}^n(-1)^if(i) i=0n(1)if(i)

    原题可以看成一个二分图的形式:( n = 5 n=5 n=5 时)

    左边是排列的编号,右边是权值,那么现在要做的就是连 n n n 条边,补全这个二分图,使得每个点的度数都是 1 1 1

    那么考虑什么时候会出现 ∣ P i − i ∣ = k |P_i-i|=k Pii=k 的情况。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kmHchJ2G-1601819094543)(https://cdn.luogu.com.cn/upload/image_hosting/5s8onaw1.png)]

    如图,当 k = 1 k=1 k=1 时,连出上图中的任意一条边都会使得 ∣ P i − i ∣ = k |P_i-i|=k Pii=k

    我们考虑选出一些边,而且任意两条边都不能接在同一个端点上(因为每个点的度数要为 1 1 1)。

    发现图中的边构成了若干条链且互不影响,于是把他们拿出来铺平:

    此时如果要使得 ∣ P i − i ∣ = k |P_i-i|=k Pii=k,只有相邻两个点之间才会连边,而且 a 5 a_5 a5 1 1 1 (第 5 5 5 个点和第 6 6 6 个点)之间不会连边。

    d p ( i , j , 0 / 1 ) dp(i,j,0/1) dp(i,j,0/1) 表示已经考虑了前 i i i 个点,其中连了 j j j 条边,第 i − 1 i-1 i1 个点和第 i i i 个点之间是/否连边的方案数。

    那么容易得到:

    { d p ( i , j , 0 ) = d p ( i − 1 , j , 0 ) + d p ( i − 1 , j , 1 ) d p ( i , j , 1 ) = d p ( i − 1 , j − 1 , 0 ) egin{cases} dp(i,j,0)=dp(i-1,j,0)+dp(i-1,j,1)\ dp(i,j,1)=dp(i-1,j-1,0) end{cases} {dp(i,j,0)=dp(i1,j,0)+dp(i1,j,1)dp(i,j,1)=dp(i1,j1,0)

    但是还有一种特殊情况,那就是 i = 6 i=6 i=6 时,第 5 5 5 个点和第 6 6 6 个点之间不能连边,所以此时 d p ( i , j , 1 ) dp(i,j,1) dp(i,j,1) 不存在。

    所以我们需要开一个数组判断一下某一个点是否是链的开头。

    按着这个 dp,那么有 f ( m ) = ( n − m ) ! × d p ( 2 n , m ) f(m)=(n-m)! imes dp(2n,m) f(m)=(nm)!×dp(2n,m)

    意思就是先把满足有 m m m ∣ P i − i ∣ = k |P_i-i|=k Pii=k 的方案数算出来,剩下的数随便排列。

    代码如下:

    #include<bits/stdc++.h>
    
    #define N 2010
    #define ll long long
    #define mod 924844033
    
    using namespace std;
    
    int n,k,tot,a[N];
    ll fac[N],dp[N<<1][N][2];
    bool vis[N<<1];
    
    int main()
    {
    	scanf("%d%d",&n,&k);
    	fac[0]=1;
    	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
    	for(int i=1;i<=k;i++)
    	{
    		for(int t=0;t<2;t++)
    		{
    			for(int j=i;j<=n;j+=k)
    			{
    				tot++;
    				if(i!=j) vis[tot]=1;
    			}
    		}
    	}
    	dp[0][0][0]=1;
    	for(int i=1;i<=(n<<1);i++)
    	{
    		for(int j=0;j<=n;j++)
    		{
    			dp[i][j][0]=(dp[i-1][j][0]+dp[i-1][j][1])%mod;
    			if(vis[i]&&j) dp[i][j][1]=dp[i-1][j-1][0];
    		}
    	}
    	ll ans=0;
    	for(int i=0;i<=n;i++)
    	{
    		if(i&1) ans=(ans-(dp[n<<1][i][0]+dp[n<<1][i][1])*fac[n-i]%mod+mod)%mod;
    		else ans=(ans+(dp[n<<1][i][0]+dp[n<<1][i][1])*fac[n-i]%mod)%mod;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    捷微商城小程序上线啦~
    JEECG 新版在线文档WIKI正式发布
    https 详解
    css 3 新特性
    js 基础(一)
    BFC
    .Net、C# 汉字转拼音,简体繁体转换方法
    丰富“WinForms” 的一个别样"项目"(学生管理)
    学生管理系统1
    学生管理系统
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14448645.html
Copyright © 2011-2022 走看看