0917考试T2 奇怪的建图方式
0917考试T2
题目大意:
你有𝑛个不同的球和𝑚个不同的盒子。每个球都被分配了两个盒子,应该放在其中一个盒子里。每个盒子只能装一个球。问题是把所有的球都放到盒子里有多少种解。
这道题竟然是图论。。。
我们可以把盒子看成节点,球看成边,那么一个球就可以连接两个(或一个节点)。
连完之后的的图会有若干个联通块,对于每个联通块:
边的数量 > 点的数量:不能保证每个球都可以放到一个盒子内,贡献为0;
边的数量 = 点的数量 - 1 :是一棵树。我们可以把每个节点都拎起来,把它作为根,每一个节点为根都是一种不同的方案,所以贡献为点的数量;
边的数量 = 点的数量 :存在一个环。如果这个环是自环,那么贡献为1,如果不是自环,那么贡献为2。
最后我们把所有联通块的贡献都乘起来就是最后总的方案数。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 3e5 + 5, mod = 998244353;
int T, n, m, l, r, ans, cnt;
int q[N], vis[N], out[N], head[N];
struct edge { int to, nxt, out; } e[N << 1];
void add(int x, int y) {
e[++cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; out[x]++;
}
int bfs(int x) {
q[l = r = 1] = x;
int sum = 0, flag = 0;
while(l <= r) {
int x = q[l++]; vis[x] = 1; sum += out[x];
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to;
if(!vis[y]) q[++r] = y, vis[y] = 1;
if(y == x) flag = 1;
}
}
sum /= 2;
if(sum == r - 1) return r;
else if(sum == r) return flag ? 1 : 2;
else return 0;
}
void work() {
m = read(); n = read();
for(int i = 1;i <= n; i++) head[i] = vis[i] = out[i] = 0; cnt = 0;
for(int i = 1, x, y;i <= m; i++) x = read(), y = read(), add(x, y), add(y, x);
ans = 1;
for(int i = 1;i <= n; i++)
if(!vis[i]) ans = 1ll * ans * bfs(i) % mod;
printf("%d\n", ans);
}
int main() {
// freopen("ball.in","r",stdin); freopen("ball.out","w",stdout);
T = read();
while(T --> 0) work();
fclose(stdin); fclose(stdout);
return 0;
}