座位安排(欧拉回路,高斯消元,bitset)

题面

由于旋转大师 F r e n c h \rm French French 的离去, A r e x t r e \rm Arextre Arextre 光荣地承担了给全班换座位的重任。

由于这是个有 O n e I n D a r k \rm OneInDark OneInDark H a n d I n D e v i l \rm HandInDevil HandInDevil 的班级,所以换座位难免有些困难。

全班一共有 n n n 个同学,有 m m m 对好友关系,当其中一对好友关系中的两人 u i , v i u_i,v_i ui,vi 不在同一个大组时,这对关系就无效。

A r e x t r e \rm Arextre Arextre 需要把全班划分为 r r r 个大组(有序),满足划分后由有效的好友关系组成的图中,每个连通块都存在欧拉回路(注意:每个大组不必连通)。

A r e x t r e \rm Arextre Arextre 已经划分好了,方案还不能公开,但是好友关系是知道的。你很好奇, r r r 的最小值是多少,以及满足 r r r 取最小值时的划分方案数是多少,方案数对 998244353 取模。

数据范围
n ≤ 2000    ,    m ≤ 1 0 5 n\leq2000\;,\;m\leq10^5 n2000,m105

样例输入

4 5
1 2
2 3
3 1
2 4
3 4

样例输出

2 4

题解

根据欧拉回路的性质,一个无向连通图存在欧拉回路当且仅当每个点的度数都为偶数

首先,如果整张图本来每个点的度数就为偶数,那么直接 {printf("1 1");return 0;} 完事,这个应该很好判断。

然后,我们可以证明 r r r 最小为 2 2 2

此时考虑怎么安排每个点,我们令 x i = 0 / 1 x_i=0/1 xi=0/1 表示组号,0 表示在 0 大组,1 表示在 1 大组,于是每个点的度数奇偶性就为

⨁ i → j ( x i ⊕ x j ⊕ 1 ) \bigoplus_{i\rightarrow j}(x_i\oplus x_j\oplus 1) ij(xixj1)

把这个线性异或方程组拿去高斯消元,若无解,也就是左边全消完,右边剩个 1。一开始的每个方程等式右边是原先度数的奇偶性,左边是邻接矩阵的第 i i i 行再在第 i i i 个位置异或度数。方程互消相当于异或,选一些方程异或起来,如果左边等于 0,相当于原图中的一个连通块、再加上若干个与该连通块连边数为偶数的点(不然不会异或出 0),连通块外的点对等式右边的贡献肯定就是偶数了,连通块内的点则满足每一条边贡献度数为 2,那么度数总和为偶数,等式右边也应该是 0。也就是说该方程组不存在无解的情况,一定有解。

既然一定有解,那么方案数就是高斯消元后的自由变元个数了。用 bitset 优化高斯消元,可以做到 O ( n 3 64 ) O(\frac{n^3}{64}) O(64n3)

CODE

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 2005
#define BT bitset<2005>
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
const int MOD = 998244353;
int n,m,i,j,s,o,k;
int g[MAXN][MAXN];
int ind[MAXN];
BT a[MAXN];
int main() {
	freopen("base.in","r",stdin);
	freopen("base.out","w",stdout);
	n = read();m = read();
	for(int i = 1;i <= m;i ++) {
		s = read();o = read();
		g[s][o] = g[o][s] = 1;
		ind[s] ++; ind[o] ++;
	}
	bool flag = 1;
	for(int i = 1;i <= n;i ++) if(ind[i] & 1) flag = 0;
	if(flag) {
		printf("1 1");
		return 0;
	}
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= n;j ++) {
			if(g[i][j]) {
				a[i][j] = a[i][j]^1;
				a[i][n+1] = a[i][n+1]^1;
				a[i][i] = a[i][i]^1;
			}
		}
	}
	int ans = 1;
	for(int i = 1;i <= n;i ++) {
		if(!a[i][i]) {
			for(int j = i+1;j <= n;j ++) {
				if(a[j][i]) {
					swap(a[i],a[j]);
					break;
				}
			}
		}
		if(!a[i][i]) {
			ans = ans *2ll % MOD;
			continue;
		}
		for(int j = 1;j <= n;j ++) {
			if(j != i && a[j][i]) {
				a[j] ^= a[i];
			}
		}
	}
	printf("%d %d\n",2,ans);
	return 0;
}
posted @ 2021-04-06 16:28  DD_XYX  阅读(43)  评论(0编辑  收藏  举报