[CF603E] Pastoral Oddities
[题目链接]
http://codeforces.com/contest/603/problem/E
[题解]
观察 : 存在一个边集满足每个点度数都是奇数的充要条件是不存在奇数个数个点的联通块。
考虑证明奇数个点的联通块一定不合法。刚开始所有点度数均为偶数。 加入 \((u , v)\) 后 :
\(u , v\) 度数均为偶数 , 加入 \((u , v)\) 后 , 奇度点数量 \(+2\)。
\(u , v\) 度数一奇一偶 , 加入 \((u , v)\) 后 , 奇度点数量不变。
\(u , v\) 度数均为奇数 , 加入 \((u , v)\) 后 , 奇度点数量 \(-2\)。
对于偶数个点的联通块 , 直接找出一棵生成树 , 从下到上确定每条 \((u , fa(u))\) 取或不取即可。
注意到每次加入一条边 , 偶数个点的联通块只增不减。
因此一个 \(O(N ^ 2logN)\) 的做法是 : 对于每个询问将边集排序 , 用并查集维护联通块奇偶性 , 依次加边加到满足条件即可。
不妨考虑整体二分。 假设我们当前处理的是 \([l , r]\) 内的边 , 且答案在 \([l , r]\) 中 , 加入时间在 \([1 , l)\) 且权值小于 \(x\) 的已经全部被加入了。 尝试求出 \(ans_{mid}\) , 这样就可以分治到两个子区间中了。
首先加入 \([l , mid]\) 内比 \(x\) 小的边 , 从小到大加入权值在 \([x , y]\) 的边 ,加到满足条件时 , 我们就求出了 \(ans_{mid}\)。
时间复杂度 : \(O(MlogMlogN)\)
[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 5e5 + 5;
struct node {
int u , v , w , home;
} E[MN] , e[MN];
int N , M , ans[MN] , home[MN];
inline bool cmp(node a , node b) {
return a.w < b.w;
}
struct DSU {
int fa[MN] , sz[MN] , tot , top , st[MN];
inline void build() {
tot = N;
for (int i = 1; i <= N; ++i) fa[i] = i , sz[i] = 1;
}
inline int find(int x) {
while (x != fa[x]) x = fa[x];
return x;
}
inline void link(int u , int v) {
u = find(u) , v = find(v); if (u == v) return;
if (sz[u] < sz[v]) swap(u , v);
fa[v] = u , tot -= (sz[u] & 1) , tot -= (sz[v] & 1);
sz[u] += sz[v]; tot += (sz[u] & 1); st[++top] = v;
}
inline void undo() {
int v = st[top--] , u = fa[v];
tot -= (sz[u] & 1); sz[u] -= sz[v]; fa[v] = v;
tot += (sz[u] & 1); tot += (sz[v] & 1);
return;
}
} S;
inline void CDQ(int l , int r , int ql , int qr) {
if (l > r) return;
int mid = l + r >> 1; int lst = S.top;
for (int i = l; i <= mid; ++i)
if (home[i] < ql) S.link(E[i].u , E[i].v);
for (int i = ql; i <= qr; ++i) {
if (e[i].home <= mid) S.link(e[i].u , e[i].v);
if (!S.tot) { ans[mid] = i; break; }
}
while (S.top > lst) S.undo();
if (!ans[mid]) {
for (int i = l; i <= mid; ++i)
if (home[i] < ql) S.link(E[i].u , E[i].v);
CDQ(mid + 1 , r , ql , qr);
while (S.top > lst) S.undo();
return;
}
for (int i = l; i <= mid; ++i)
if (home[i] < ql) S.link(E[i].u , E[i].v);
CDQ(mid + 1 , r , ql , ans[mid]);
while (S.top > lst) S.undo();
for (int i = ql; i < ans[mid]; ++i)
if (e[i].home < l) S.link(e[i].u , e[i].v);
CDQ(l , mid - 1 , ans[mid] , qr);
while (S.top > lst) S.undo();
}
int main() {
scanf("%d%d" , &N , &M); S.build();
for (int i = 1; i <= M; ++i) {
scanf("%d%d%d" , &e[i].u , &e[i].v , &e[i].w);
e[i].home = i;
}
for (int i = 1; i <= M; ++i) E[i] = e[i];
sort(e + 1 , e + 1 + M , cmp);
for (int i = 1; i <= M; ++i)
home[e[i].home] = i;
CDQ(1 , M , 1 , M);
for (int i = 1; i <= M; ++i)
if (!ans[i]) printf("-1\n");
else printf("%d\n" , e[ans[i]].w);
return 0;
}