• Three Permutations [ABC214G]


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

    题解

    考虑容斥,计算钦定 (k) 位满足 (r_i=p_i | r_i=q_i) 的方案数

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

    对于自环,那么肯定有 (p_i=q_i),只用考虑 (r_i) 是否等于 (p_i)

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

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

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

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

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

    对于环,如果没选择 ((1,n)) 这组匹配,方案数就是 (inom{n-k}{k}),选了的话就把 (1,n) 两个点删掉,还要选 (k-1) 组匹配,方案数是 (inom{n-2-(k-1)}{k-1}=inom{n-k-1}{k-1})

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

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

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

    [Ans=sumlimits_{i=0}^n (-1)^i*f_i*(n-i)! ]

    代码

    #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
    ", ans);
    	return 0;
    }
    
    作者:AK_DREAM
    本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    网页中让flash在最底层显示
    在asp.net mvc模式下怎么实现导出Excel文件
    Tomcat的内存溢出(PermGen space)
    CentOS下安装7zip
    CentOS安装keychain
    HTML中添加水平滚动文字
    getOutputStream() has already been called for this response的解决方法
    小型服务器的分区方案
    Mysql导出存储过程
    CentOS安装FileZilla时找不到libtasn1.so.3
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_Dream123.html
Copyright © 2020-2023  润新知