组合计数+容斥:1024T2

本文搬运自本人高中时期CSDN博客,若图片加载不出来,可到原文查看:https://blog.csdn.net/zhangtingxiqwq/article/details/134013962

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

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

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


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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#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;
}