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;
}

posted @ 2020-09-18 06:10  C锥  阅读(149)  评论(0编辑  收藏  举报