[UOJ513]【UR #19】清扫银河
非常有意思的一道题,UR 还是一如既往的高水平……就是我比较菜,赛后 15min 突然一拍大腿想出来怎么做了(
首先有一个结论:对于任意图,如果存在方案,一定可以在 \(m+1\) 步内完成。
证明:首先考虑操作 \(2\)。先假设所有点都是黑色,逐渐将某些点染白的过程。发现一条边 \((x, y)\) 在染白 \(x\) 时会被翻转,而在染白 \(y\) 时又会被翻回去。这等价于,将一个点染白时,翻转它所有的连边。因此,每个点最优策略中只会被染白一次,也就是说,\(2\) 操作只会进行一次,这一次会将所有应该被翻转的点染白。
再考虑操作 \(1\)。对每个连通块任意构造一棵生成树,则每个非树边唯一对应了一个简单环。不难发现,图中任意一个简单环都可以由上述简单环异或得到(只需要取出这个简单环中,每条非树边对应的环即可)。换句话说,对于任何一种操作 \(1\) 中选择简单环的方式,都只需要将最终状态中被翻转的非树边对应的环取出来异或。因此操作 \(1\) 的数量不会超过非树边数量,且一定 \(<m\)。
因此,我们只需要判定给定的图是否存在至少一个方案即可。
假设我们已经选择了 \(2^n\) 个点集中的一个进行了操作 \(2\),考虑如何判定是否能通过操作 \(1\) 变为全 \(0\)。
首先发现若干简单环构成的图,每个点的度数都是偶数。而这恰好是无向图拥有若干不相交欧拉回路的充要条件。又因为每个欧拉回路都能拆成若干简单环,所以一个图由若干简单环构成,等价于它的每个点度数都是偶数。
也就是说我们要求操作 \(2\) 后,每个点都连接了偶数条不能通行的边。再考虑将一个点染白的影响:任何与它直接连边的点,不能通行的边的奇偶性都会改变。如果这个点本身连边数量为奇数,那么它本身的奇偶性也会改变。也就是说,每个点对应了一个长度为 \(n\) 的 \(01\) 向量,现在我们需要选出若干向量,使得他们的异或和等于初始状态下,每个点不能通行的边的奇偶性组成的 \(01\) 向量。这个问题可以用线性基简单解决,复杂度 \(O(\frac{n^3}{w})\)。
Code:
#include <bits/stdc++.h>
#define R register
#define mp make_pair
#define ll long long
#define pii pair<int, int>
using namespace std;
const int mod = 998244353, N = 320;
int t, n, m;
bitset<N> bs[N], to[N], dgr;
inline int addMod(int a, int b) {
return (a += b) >= mod ? a - mod : a;
}
inline ll quickpow(ll base, ll pw) {
ll ret = 1;
while (pw) {
if (pw & 1) ret = ret * base % mod;
base = base * base % mod, pw >>= 1;
}
return ret;
}
template <class T>
inline void read(T &x) {
x = 0;
char ch = getchar(), w = 0;
while (!isdigit(ch)) w = (ch == '-'), ch = getchar();
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
x = w ? -x : x;
return;
}
int insrt(bitset<N> &x) {
for (R int i = n; i; --i) {
if (!x[i]) continue;
if (bs[i].none()) return bs[i] = x, 1;
x ^= bs[i];
}
return 0;
}
int main() {
int x, y, w;
read(t);
while (t--) {
read(n), read(m);
dgr.reset();
for (R int i = 1; i <= n; ++i) bs[i].reset(), to[i].reset();
for (R int i = 1; i <= m; ++i) {
read(x), read(y), read(w);
to[x][y] = to[y][x] = 1;
to[x][x].flip(), to[y][y].flip();
dgr[x] = dgr[x] ^ w, dgr[y] = dgr[y] ^ w;
}
for (R int i = 1; i <= n; ++i) insrt(to[i]);
printf("%s\n", insrt(dgr) ? "no" : "yes");
}
return 0;
}