[AGC005D] ~K Perm Counting

\(\text{Problem}:\)[AGC005D] ~K Perm Counting

\(\text{Solution}:\)

普通的错排问题,考虑容斥求解。

\(f_{i}\) 表示恰好有 \(i\) 个位置满足 \(\lvert P_{i}-i\rvert=k\)\(g_{i}\) 表示钦定 \(i\) 个位置满足 \(\lvert P_{i}-i\rvert=k\),有:

\[f_{i}=\sum\limits_{j=i}^{n}\binom{i}{j}(-1)^{j-i}g_{j} \]

\(i=0\) 时,有 \(f_{0}=\sum\limits_{i=0}^{n}(-1)^{i}g_{i}\)

转化 \(1\):将 \(P_{i}\)\(i\) 分开考虑,其限制关系构成一个二分图。求钦定 \(k\) 个匹配时的方案数。

转化 \(2\):将 \(P_{i}\)\(i\) 分开考虑,其限制关系对应前 \(i\) 个位置选了 \(j\) 组,求最后选择 \(k\) 组的方案数。

不难发现上面两种转化是等价的。考虑将连边关系展开,形成了总长和为 \(2n\) 的若干条链。现在变成在若干条链上选 \(k\) 个边,使得这 \(k\) 条边无交点。

\(f_{i,j,0/1}\) 表示前 \(i\) 个点选了 \(j\) 条边,\(i\) 位置是否为一条选择了的边的端点。总时间复杂度 \(O(n^2)\)

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=2010, Mod=924844033;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) w=-1; ch=getchar(); }
	while(ch>=‘0‘&&ch<=‘9‘) s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,K,f[N*2][N][2],book[N*2],cnt,vis[N*2],fac[N+5],ans;
signed main()
{
	n=read(), K=read();
	fac[0]=1;
	for(ri int i=1;i<=N;i++) fac[i]=1ll*fac[i-1]*i%Mod;
	for(ri int j=1;j<=n+n;j++)
	{
		int i;
		if(j&1) i=(j+1)/2;
		else i=j/2+n;
		if(book[i]) continue;
		int go=i;
		while(!book[go])
		{
			book[go]=1, cnt++;
			if(go<=n)
			{
				if(go+K>n) break;
				else go=go+n+K; 
			}
			else
			{
				if(go+K>n+n) break;
				else go=go-n+K;
			}
		}
		vis[cnt]=1;
	}
	f[0][0][0]=1;
	for(ri int i=1;i<=n+n;i++)
	{
		for(ri int j=0;j<=n;j++)
		{
			if(!j) { f[i][j][0]=f[i-1][j][0]; continue; }
			f[i][j][0]=(f[i-1][j][0]+f[i-1][j][1])%Mod;
			if(!vis[i]) f[i][j][1]=f[i-1][j-1][0];
		}
	}
	for(ri int i=0;i<=n;i++)
	{
		int w=1ll*(f[n+n][i][0]+f[n+n][i][1])*fac[n-i]%Mod;
		if(i&1) ans=(ans-w+Mod)%Mod;
		else ans=(ans+w)%Mod;
	}
	printf("%d\n",ans);
	return 0;
}

[AGC005D] ~K Perm Counting

上一篇:使用windows消息队列MessageQueue


下一篇:VS2019配置MKL教程(Windows)