题面
由于旋转大师 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
n≤2000,m≤105
样例输入
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) i→j⨁(xi⊕xj⊕1)
把这个线性异或方程组拿去高斯消元,若无解,也就是左边全消完,右边剩个 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;
}