[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;
}
posted @ 2020-02-25 23:04  suwakow  阅读(246)  评论(0编辑  收藏  举报
Live2D