[CF1299D] Around the World
挺有趣的题目,好像也不算难,不知道为什么场上没想出来。
先随便弄一棵生成树,随后每条非树边都对应了一个简单环。可以证明任意的回路中,经过了奇数次的边集可以由这些环 \(\operatorname{xor}\) 出来。因此每个环可以作为线性基中的一个向量;如果将某个环插入线性基的过程中,出现了线性相关的情况,则将线性相关的子集提出来,这种方案会使得权值为 \(0\)。
利用状态压缩记录线性基的状态,依次处理删掉 \(1\) 号点之后,剩下的每个连通块。对于只有一条边与 \(1\) 号点相连的块,如果删去这条边则状态不会变化,否则将两个线性基合并。对于有两条边的块,只删一条边会破坏掉其中一个环,其余状态类似。
直接状态压缩,即使用 map
维护有用状态也无法承受。但是发现有很多线性基可以通过初等变换变成相同的状态。因此每个状态先变换成某种最简形式再进行状压,状态数会大大减少,小于 \(10^3\)。
我写的 map
常数略大,换成 vector
才过……
#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 N = 110000, M = N << 1, mod = 1e9 + 7;
int n, m, hd[N], dep[N], sum[N], nxt[M], to[M], val[M], noedg = 1, num, minC;
vector<pii> cir, f, tmp;
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;
}
inline void addEdg(int x, int y, int w) {
nxt[++noedg] = hd[x], hd[x] = noedg, to[noedg] = y, val[noedg] = w;
nxt[++noedg] = hd[y], hd[y] = noedg, to[noedg] = x, val[noedg] = w;
return;
}
void dfs(int now, int fa) {
dep[now] = dep[fa] + 1;
for (R int i = hd[now], v; i; i = nxt[i]) {
if ((v = to[i]) == fa) continue;
if (dep[v]) {
if (dep[v] > dep[now]) continue;
if (v != 1) ++num, minC = min(minC, sum[v] ^ sum[now] ^ val[i]);
cir.push_back(mp(sum[v] ^ sum[now] ^ val[i], v == 1));
}
else
sum[v] = sum[now] ^ val[i], dfs(v, now);
}
return;
}
inline int siz(int x) {
return _popcnt32(x >> 25);
}
inline int addMod(int a, int b) {
return (a += b) >= mod ? a - mod : a;
}
inline int insrt(int p, int v) {
for (R int i = 4; ~i; --i)
if ((v & (1 << i)) && (p & (1 << i << 25)))
v ^= (p >> (5 * i)) & 31;
if (!v) return -1;
for (R int i = 4; ~i; --i) {
if (v & (1 << i)) {
for (R int j = i + 1; j <= 4; ++j)
if ((p >> (5 * j + i)) & 1) p ^= v << (5 * j);
p ^= v << (5 * i), p ^= 1 << (25 + i);
return p;
}
}
return -1;
}
int main() {
int x, y, w;
read(n), read(m);
for (R int i = 1; i <= m; ++i)
read(x), read(y), read(w), addEdg(x, y, w);
dep[1] = 1, f.push_back(mp(0, 1));
int pw = 0;
for (R int i = hd[1], v; i; i = nxt[i]) {
if (dep[v = to[i]]) continue;
cir.clear();
sum[v] = val[i], num = 0, minC = 32, dfs(v, 1);
if (!cir.size()) {
++pw;
continue;
}
if (!minC || num > 5) continue;
tmp = f;
for (auto &k : f) {
if (siz(k.first) + num > 5) continue;
int p = k.first, flag = 1, val = -1;
for (auto &v : cir) {
if (v.second) {
val = v.first;
continue;
}
p = insrt(p, v.first);
if (p == -1) {
flag = 0;
break;
}
}
if (!flag) continue;
if (val != -1) {
tmp.push_back(mp(p, addMod(k.second, k.second)));
p = insrt(p, val);
if (p != -1) tmp.push_back(mp(p, k.second));
}
else
tmp.push_back(mp(p, k.second));
}
sort(tmp.begin(), tmp.end());
f.clear();
for (auto &k : tmp) {
if (f.size() && f.back().first == k.first)
f.back().second = addMod(f.back().second, k.second);
else
f.push_back(k);
}
}
int ans = 0;
for (auto &k : f) ans = addMod(ans, k.second);
while (pw) ans = addMod(ans, ans), --pw;
cout << ans << endl;
return 0;
}