组合计数+容斥:1024T2

http://cplusoj.com/d/senior/p/SS231024B?tid=653748c7611b23c4594b05ab

要么是环,要么是链,都可以有两个方向

链的话可以缩成点一起统计


长为2的链如果成二元环不应该乘2,那么考虑容斥

g ( i ) g(i) g(i) 表示至少有 i i i 个二元环不被算错的方案数, ∑ i = 0 k g ( i ) ( − 1 ) k − i \sum_{i=0}^kg(i)(-1)^{k-i} i=0kg(i)(1)ki

g ( i ) = ( k i ) ( z + i ) ! 2 i g(i)=\binom k i(z+i)!2^i g(i)=(ik)(z+i)!2i

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
//mt19937 rand(time(0));
//mt19937_64 rand(time(0));
//srand(time(0));
#define N 500010
//#define M
#define mo 998244353
int pw(int a, int b) {
	int ans=1; 
	while(b) {
		if(b&1) ans*=a; 
		a*=a; b>>=1; 
		ans%=mo; a%=mo; 
	}
	return ans; 
}
int fac[N], inv[N], ifac[N]; 
void init(int n) {
	int i; 
	for(i=fac[0]=1; i<=n; ++i) fac[i]=fac[i-1]*i%mo; 
	ifac[n]=pw(fac[n], mo-2); 
	for(i=n-1; i>=0; --i) ifac[i]=ifac[i+1]*(i+1)%mo; 
    for(i=1; i<=n; ++i) inv[i]=ifac[i]*fac[i-1]%mo; 
}
int C(int n, int m) {
	if(m>n) return 0;
	return fac[n]*ifac[m]%mo*ifac[n-m]%mo; 
}
void Add(int &a, int b) {
	a+=b; if(a>=mo || a<=-mo) a%=mo; 
    if(a<0) a+=mo; 
}
void Mul(int &a, int b) {
	a*=b; if(a>=mo || a<=-mo) a%=mo; 
    if(a<0) a+=mo; 
}
void Mod(int &a) {
	if(a>=mo || a<=-mo) a%=mo; 
    if(a<0) a+=mo; 
}
const int iv2=pw(2, mo-2); 
int n, m, i, j, k, T, f[N], c[N], x, y;
int ans, sum, w[N], h[N], z; 
map<pair<int, int>, int>mp; 

int fa(int x) {
	if(f[x]==x) return x; 
	return f[x]=fa(f[x]); 
}

signed main()
{
//	freopen("in.txt", "r", stdin);
//	freopen("out.txt", "w", stdout);
	freopen("per.in", "r", stdin);
	freopen("per.out", "w", stdout);
//	T=read();
//	while(T--) {
//
//	}
	n=read(); m=read(); init(n); ans=1; 
	for(i=1; i<=n; ++i) f[i]=i, w[i]=0; 
	for(i=1; i<=m; ++i) {
		x=read(); y=read(); 
		if(mp[{x, y}]) continue; 
		mp[{x, y}]=mp[{y, x}]=1; 
//		if(fa(x)==fa(y)) return printf("0"), 0; 
		if(fa(x)!=fa(y)) w[fa(y)]+=w[fa(x)]; 
		f[fa(x)]=fa(y); w[fa(y)]++; 
		++c[x]; ++c[y]; 
		if(c[x]>2 || c[y]>2)  return printf("0"), 0; 
	}
	for(i=1; i<=n; ++i) h[fa(i)]++; 
	for(i=1, k=0; i<=n; ++i) 
		if(fa(i)==i) {
			if(!w[i]) ++z; //单点 
			else if(h[i]==1) continue; //自环 
			else if(h[i]==w[i]) Mul(ans, 2); //环
			else if(h[i]==2) ++k; //双点链
			else ++z, Mul(ans, 2); //正常链 
		}
//	printf("%lld %lld | %lld\n", z, k, ans); 
	sum=0; 
	for(i=0; i<=k; ++i) {
		if((k-i)&1) Add(sum, -C(k, i)*fac[i+z]%mo*pw(2, i)%mo); 
		else Add(sum, C(k, i)*fac[i+z]%mo*pw(2, i)%mo); 
	}
	
	printf("%lld", ans*sum%mo); 
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值