Description D e s c r i p t i o n
- 有 n n 个点的图,你要动态的加入条边。
- 每次加边后询问图中找到一个最大边最小的子图使得每个点的度数都是奇数。
- 输出最大边的最小值。
- n≤105,m≤3×105 n ≤ 10 5 , m ≤ 3 × 10 5
Solution S o l u t i o n
有一个结论:满足条件的图当且仅当每个联通块大小为偶数。
又是这个
idea
idea
哦。证明的话可以考虑图的生成树,从下至上调整。
然后答案一定是非上升的。
可以考虑分治。道理是和决策单调性DP分治差不多的
定义分治
DivAndConq(l,r,lo,hi)
D
i
v
A
n
d
C
o
n
q
(
l
,
r
,
l
o
,
h
i
)
表示当前分治的边集为
[l,r]
[
l
,
r
]
,答案在
[lo,hi]
[
l
o
,
h
i
]
中。
这里有一张图很好的说明了分治结构。
因为要维护联通块个数的奇偶性,就要用并查集。
这个就和这道题有些类似啦。
那其实这道题就也是可以用LCT做的了。
并查集的时间复杂度是
o(mlogmlogn)
o
(
m
log
m
log
n
)
的。
#include <bits/stdc++.h>
using namespace std;
const int N = 101010;
const int M = 303030;
typedef long long ll;
inline char get(void) {
static char buf[100000], *S = buf, *T = buf;
if (S == T) {
T = (S = buf) + fread(buf, 1, 100000, stdin);
if (S == T) return EOF;
}
return *S++;
}
template<typename T>
inline void read(T &x) {
static char c; x = 0;
for (c = get(); c < '0' || c > '9'; c = get());
for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}
struct Qry {
int x, y, l, id;
inline bool operator <(const Qry &a) const{
return l < a.l;
}
};
Qry p[M], q[M];
int n, m;
int ans[M];
namespace dsu {
int fa[N], rk[N], sz[N];
int sta[N], drk[N], dsz[N], dcnt[N];
int top, cnt;
inline void Init(int n) {
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= n; i++) sz[i] = 1;
cnt = n;
}
inline int Fa(int x) {
while (fa[x] != x) x = fa[x];
return x;
}
inline int Merge(int x, int y) {
static int f1, f2;
f1 = Fa(x); f2 = Fa(y);
if (f1 == f2) return 0; ++top;
dsz[top] = dcnt[top] = drk[top] = 0;
if (rk[f1] > rk[f2]) swap(f1, f2);
if (rk[f1] == rk[f2]) {
++rk[f2]; drk[top] = 1;
}
fa[f1] = f2; sta[top] = f1;
if (sz[f1] && sz[f2]) {
cnt -= 2; dcnt[top] = -2;
}
dsz[top] = sz[f1]; sz[f2] ^= sz[f1];
return 1;
}
inline void Restore(int pos) {
while (top > pos) {
cnt -= dcnt[top];
rk[fa[sta[top]]] -= drk[top];
sz[fa[sta[top]]] ^= dsz[top];
fa[sta[top]] = sta[top]; --top;
}
}
inline bool Check(void) {
return !cnt;
}
inline void Record(int &lst) {
lst = top;
}
}
inline void DivAndConq(int l, int r, int lo, int hi) {
int mid = l + r >> 1, mans = -1, lst;
if (l > r) return; dsu::Record(lst);
for (int i = l; i <= mid; i++)
if (q[i].id < lo) dsu::Merge(q[i].x, q[i].y);
for (int i = lo; i <= hi; i++)
if (p[i].id <= mid) {
dsu::Merge(p[i].x, p[i].y);
if (dsu::Check()) {
mans = i; break;
}
}
dsu::Restore(lst);
if (mans == -1) {
for (int i = l; i <= mid; i++) ans[i] = -1;
for (int i = l; i <= mid; i++)
if (q[i].id < lo) dsu::Merge(q[i].x, q[i].y);
DivAndConq(mid + 1, r, lo, hi);
return dsu::Restore(lst);
}
ans[mid] = p[mans].l;
for (int i = l; i <= mid; i++)
if (q[i].id < lo) dsu::Merge(q[i].x, q[i].y);
DivAndConq(mid + 1, r, lo, mans);
dsu::Restore(lst);
for (int i = lo; i < mans; i++)
if (p[i].id < l) dsu::Merge(p[i].x, p[i].y);
DivAndConq(l, mid - 1, mans, hi);
dsu::Restore(lst);
}
int main(void) {
read(n); read(m);
for (int i = 1; i <= m; i++) {
read(p[i].x); read(p[i].y); read(p[i].l);
p[i].id = i; q[i] = p[i];
}
sort(p + 1, p + m + 1);
for (int i = 1; i <= m; i++)
q[p[i].id].id = i;
dsu::Init(n);
DivAndConq(1, m, 1, m);
for (int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
return 0;
}