http://codeforces.com/problemset/problem/603/E
题解:
先思考对于一个图怎么样是合法的?
如果这个图有奇数大小的联通块,显然不行:
因为一条边会使度数和+2,度数和始终是偶数,而奇数大小联通块要求的度数和是奇数。
大胆猜测这也是充分条件。
证明的话,对于每一个欧联通块,保留一个生成树。
自下而上的看每一个点和父亲的边是是否要保留,由度数的性质可以归纳得一定有解。
1.lct做法
考虑维护每一个前缀的mst,这个可以用lct做。
当不存在奇联通块后,可以考虑删掉一些边,使得剩下的森林依然合法。
那么要使答案减小,肯定要从最大的边中删,所以就用个堆维护mst中边,每次取出最大的,lct同时维护子树和,以判断一条边能不能删。
注意一定要当森林合法后,才开始删边,不然会因为不能把一些奇联通块合并起来导致答案挂掉。
2.线段树分治
先求整个图的答案,得到(ans[m])。
考虑把权值(le ans[m])的边,扔进线段树,区间为[它的编号,m]
然后线段树分治,从大到小跑叶子,把经过的边加入并查集。
当到一个叶子节点x时,有奇联通块时,说明只用之前的边是不够的,要扩大答案,那么把>ans的边依次加进来,直到合法未知,同时把这些边加入线段树区间[它的编号,x-1],并把这些边从并查集撤掉.
并查集当然要按秩合并。
3.整体二分:
(solve(x,y,l,r))表示前缀[x,y]的答案在[l,r]里。
设(m=(l+r)/2)
如果每次重新判断,那么需要遍历1-y的边,这显然超时。
那么在solve函数之前就可以把(编号in[1,x-1],权值in[1,l-1])的边加入并查集,这些边在接下来的递归肯定也是用的上的。
进来后,再把(编号in[1,x-1],权值in[l,m])的边加入并查集。
然后枚举(x->y)中(权值<=m)的边,加入并查集,直到不存在奇联通块,设这个分界点是t。
则需要(solve(x,t-1,m+1,r),solve(t,y,l,m))
在(solve(t,y,l,m))前,把(x->t-1)中(权值<=x)的边加入并查集
并查集还是要按秩合并的。
Code:
lct的。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;
const int N = 4e5 + 5;
int n, m;
#define x0 t[x][0]
#define x1 t[x][1]
int fa[N], t[N][2], pf[N], rev[N], dd[N];
int siz[N], mx[N], z[N], g[N], f[N];
void upd(int x) {
if(x) {
mx[x] = x;
if(z[mx[x0]] > z[mx[x]]) mx[x] = mx[x0];
if(z[mx[x1]] > z[mx[x]]) mx[x] = mx[x1];
f[x] = g[x] + f[x0] + f[x1] + (x <= n);
}
}
void fan(int x) { if(x) swap(x0, x1), rev[x] ^= 1;}
void down(int x) { if(rev[x]) fan(x0), fan(x1), rev[x] = 0;}
int lr(int x) { return t[fa[x]][1] == x;}
void ro(int x) {
int y = fa[x], k = lr(x);
t[y][k] = t[x][!k]; if(t[x][!k]) fa[t[x][!k]] = y;
fa[x] = fa[y]; if(fa[y]) t[fa[y]][lr(y)] = x;
fa[y] = x; t[x][!k] = y; pf[x] = pf[y];
upd(y); upd(x);
}
void xc(int x) {
for(; x; x = fa[x]) dd[++ dd[0]] = x;
while(dd[0]) down(dd[dd[0] --]);
}
void sp(int x, int y) {
xc(x);
for(; fa[x] != y; ro(x)) if(fa[fa[x]] != y)
ro(lr(x) == lr(fa[x]) ? fa[x] : x);
}
void ac(int x) {
for(int y = 0; x; ) {
sp(x, 0), fa[x1] = 0, pf[x1] = x;
g[x] += f[x1];
x1 = y, fa[y] = x, pf[y] = 0;
g[x] -= f[x1];
y = x, upd(x), x = pf[x];
}
}
void mr(int x) {
ac(x); sp(x, 0); fan(x);
}
void link(int x, int y) {
mr(x); mr(y);
pf[x] = y; g[y] += f[x];
ac(x);
}
void cut(int x, int y) {
mr(x); ac(y); sp(y, 0);
fa[x] = pf[x] = t[y][0] = 0;
upd(y);
}
int fl(int x) {
down(x);
return x0 ? fl(x0) : x;
}
struct edge {
int x, y, z;
} e[N];
int td;
struct P {
int v, x;
P(int _v = 0, int _x = 0) {
v = _v, x = _x;
}
};
bool operator < (P a, P b) {
if(a.v == b.v) return a.x < b.x;
return a.v < b.v;
}
multiset<P> s;
int cntj;
void work(int w) {
int x = e[w].x, y = e[w].y;
mr(x); ac(y); sp(y, 0);
int x2 = fl(y);
if(x == x2) {
sp(x, 0);
int t = mx[x];
if(z[w] < z[t]) {
cut(e[t].x, t); cut(e[t].y, t);
s.erase(P(z[t], t));
link(x, w);
link(w, y);
s.insert(P(z[w], w));
}
} else {
mr(x); mr(y);
cntj -= f[x] & 1;
cntj -= f[y] & 1;
cntj += (f[x] + f[y]) & 1;
link(x, w); link(w, y);
s.insert(P(z[w], w));
}
}
int era(int w) {
int x = e[w].x, y = e[w].y;
mr(x); ac(w); sp(x, 0);
int sy = f[w], sx = f[x] - f[w];
if(sx % 2 == 0 && sy % 2 == 0) {
cut(x, w); cut(y, w);
s.erase(P(z[w], w));
return 1;
}
return 0;
}
int main() {
scanf("%d %d", &n, &m);
td = n; cntj = n;
fo(i, 1, n) f[i] = 1;
fo(ii, 1, m) {
td ++;
scanf("%d %d %d", &e[td].x, &e[td].y, &e[td].z);
z[td] = e[td].z;
work(td);
if(cntj == 0) {
while(!s.empty()) {
P a = *(--s.end());
if(!era(a.x)) break;
}
}
if(cntj > 0) {
pp("-1
");
} else {
pp("%d
", (*s.rbegin()).v);
}
}
}