Three Permutations [ABC214G]

https://atcoder.jp/contests/abc214/tasks/abc214_g

题解

考虑容斥,计算钦定 k 位满足 ri=pi  ri=qi 的方案数

我们建出 n 个点,将每对 pi,qi 之间连一条边,由于每个点的度数都是 2 ,所以会形成若干个环和一些孤立点(自环)

对于自环,那么肯定有 pi=qi,只用考虑 ri 是否等于 pi

不考虑自环,那么选出 k 个位置产生冲突就等价于在图上选择 k 条边,并且给每条被选择的边分配一个它的端点,并且每个点只能分配给一条边

显然可以把图中的每个环分开考虑

如果把环上的边也看成点,对于原先的一条边 (u,v) ,断开 (u,v) 并新建点 w,连接 (u,w),(w,v) ,容易看出修改后的图仍然是环,此时选出 k 个位置就等价于选择 k 对匹配点

考虑在一个大小为 n 的环中选择 k 对匹配有多少种方案:

如果是 n 个点的链,那么先在 nk 个点里面选择 k 个点当匹配的右端点,然后把剩下的 k 个点插到选择的 k 个点的前面,形成 k 组匹配,方案数是 (nkk)

对于环,如果没选择 (1,n) 这组匹配,方案数就是 (nkk),选了的话就把 1,n 两个点删掉,还要选 k1 组匹配,方案数是 (n2(k1)k1)=(nk1k1)

所以,对于原问题的一个大小为 c 的环,选出 k 个位置冲突的方案数就是 (2ckk)+(2ck1k1)

用背包 O(n2) 合并每个环的方案数,计算出对于整个图,选出 k 个位置冲突的方案数,记为 fk,对于未冲突的位置可以随便选,方案数是 (nk)!

答案即为没有任何位置冲突的方案数,二项式反演可得

Ans=i=0n(1)ifi(ni)!

代码

#include <bits/stdc++.h>
#define N 6005
using namespace std;

template <typename T>
inline void read(T &num) {
	T x = 0, f = 1; char ch = getchar();
	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x<<1) + (x<<3) + (ch^'0');
	num = x * f;
}

const int mod = 1000000007;
inline int qmod(int x) { return x<mod?x:x-mod; }
inline int fpow(int x, int t) { int r=1;for(;t;t>>=1,x=1ll*x*x%mod)if(t&1)r=1ll*r*x%mod;return r; }
int n, a[N], b[N], fac[N], Inv[N], vis[N];
int f[N], g[N], tt;
inline int C(int p, int q) { return (p<0||q<0||p<q)?0:1ll*fac[p]*Inv[q]%mod*Inv[p-q]%mod; }

int main() {
	read(n);
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1; i <= n; i++) read(b[a[i]]);
	fac[0] = Inv[0] = 1;
	for (int i = 1; i <= 2*n+1; i++) fac[i] = 1ll*fac[i-1]*i%mod;
	Inv[2*n+1] = fpow(fac[2*n+1], mod-2);
	for (int i = 2*n; i; i--) Inv[i] = 1ll*Inv[i+1]*(i+1)%mod;
	f[0] = 1;
	for (int k = 1; k <= n; k++) if (!vis[k]) {
		int cnt = 0;
		for (int i = k; !vis[i]; i = b[i]) vis[i] = 1, ++cnt;
		memset(g, 0, sizeof(g));
		if (cnt == 1) {
			g[0] = f[0];
			for (int i = 1; i <= tt+1; i++) g[i] = qmod(f[i-1]+f[i]);
		} else {
			memset(g, 0, sizeof(g));
			for (int i = 0; i <= tt; i++) for (int j = 0; j <= cnt; j++) {
				g[i+j] = qmod(g[i+j]+1ll*f[i]*qmod(C(2*cnt-j,j)+C(2*cnt-j-1,j-1))%mod);
			}
		}
		memcpy(f, g, sizeof(f)); tt += cnt;
	}
	int ans = 0;
	for (int i = 0; i <= n; i++) {
		f[i] = 1ll*f[i]*fac[n-i]%mod;
		if (i & 1) ans = qmod(ans+mod-f[i]);
		else ans = qmod(ans+f[i]);
	}
	printf("%d\n", ans);
	return 0;
}
posted @   AK_DREAM  阅读(360)  评论(1编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示