「codeforces - 687D」Dividing Kingdom II
好题啊。
首先有一个类 kruskal 暴力,就是对于每一个询问,把所有边按权值大小排降序,第一个加进去成为奇环的边就是答案。注意我们不需要关注偶环长成什么样子,所以我们实际上维护的是一棵生成树。这个可以用并查集维护结点到根的边的数量来实现。
因此我们需要关注的边只有 \(O(n)\) 条了(生成树),那么维护一个区间的需要关注的边的边集,具体而言就是树边和奇环上的边。考虑用线段树,合并的时候用归并即可。
int n, m, q, fa[2100], dst[2100], ms, mh;
int u[1000100], v[1000100], w[1000100];
vi<bsi<int>> md;
bool flag;
int ldr(int x) {
if (x != fa[x]) {
int u = ldr(fa[x]);
dst[x] ^= dst[fa[x]];
return fa[x] = u;
}
return x;
}
int mrg(int x, int y) {
int u = ldr(x), v = ldr(y);
if (u == v) {
if (dst[x] == dst[y]) return 2;
return 0;
}
fa[v] = u;
dst[v] = dst[x]^dst[y]^1;
return 1;
}
bsi<int> mrg(const bsi<int>& lhs, const bsi<int>& rhs) {
bsi<int> qwq, res;
merge(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), back_inserter(qwq), [&](int x, int y) {
return w[x] > w[y];
});
for (auto i : qwq) {
fa[u[i]] = u[i], fa[v[i]] = v[i];
dst[u[i]] = dst[v[i]] = 0;
}
for (auto i : qwq) {
int tmp = mrg(u[i], v[i]);
if (tmp) res += i;
if (tmp == 2) {
flag = 1;
break;
}
}
return res;
}
void upd(int now) {
md[now] = mrg(md[now*2], md[now*2+1]);
}
int qry(int l, int r) {
bsi<int> res;
flag = 0;
for (l += ms-1, r += ms; l < r; l >>= 1, r >>= 1) {
if (l&1) res = mrg(res, md[l++]);
if (r&1) res = mrg(md[--r], res);
}
if (flag) return w[res.back()];
return -1;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> q;
mh = ceil(log2(m)), ms = 1<<mh;
md = vi<bsi<int>>(ms*2, bsi<int>());
for (int i=1; i<=m; ++i) {
cin >> u[i] >> v[i] >> w[i];
}
for (int i=1; i<=m; ++i) {
md[i+ms-1] = bsi<int>({i});
}
for (int i=ms-1; i>=1; --i) {
upd(i);
}
for (int l,r; q--;) {
cin >> l >> r;
cout << qry(l, r) << "\n";
}
}