【题目】
添加链接描述
一幅
n
n
n个点的图,动态加入
m
m
m条边,每次加边后你需要找到一个最大边权最小的边集使得每个点的度数都是奇数,求这个最大边权。
n
≤
1
0
5
,
m
≤
3
×
105
n\leq 10^5,m\leq 3\times 105
n≤105,m≤3×105
【解题思路】
我们可以发现一些性质:
如果使得图中每个点度数都为奇数,那么每个连通块中的点个数一定为偶数。这个我们可以考虑一棵生成树,那么根节点一定有奇数个儿子,非根节点一定有偶数个儿子。
对于一个偶连通块,一组合法方案就是求出其一棵生成树,然后从叶子往上考虑每个节点的度数即可,方案显然是唯一的。
还可以发现加边一定不会使得答案变差,这个也比较显然。
于是我们只需要支持动态加边删边路径最大边权子树大小?上
LCT
\text{LCT}
LCT好像就可以了。
WC要维护子树信息
复杂度 O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10,inf=0x3f3f3f3f;
int n;
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(int x){if(x<0)x=-x,putchar('-');if(x>9)write(x/10);putchar(x%10^48);}
void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;
namespace Data_Structure
{
struct Tway{int u,v;}e[N];
struct data
{
int val,id;
data(int _v=0,int _i=0):val(_v),id(_i){}
bool operator <(const data&rhs)const{return val<rhs.val || (val==rhs.val && id<rhs.id);}
bool operator ==(const data&rhs)const{return val==rhs.val && id==rhs.id;}
};
struct Heap
{
priority_queue<data>q,del;
void insert(const data&A){q.push(A);}
void erase(const data&A){del.push(A);}
data top()
{
while(q.size() && del.size() && q.top()==del.top()) q.pop(),del.pop();
return q.size()?q.top():data(-1,0);
}
}Q;
int top,st[N];
struct LCT
{
#define ls ch[x][0]
#define rs ch[x][1]
int fa[N],siz[N],isiz[N],rev[N],mx[N],val[N],ch[N][2];
int get(int x){return ch[fa[x]][1]==x;}
bool isroot(int x){return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;}
void pushup(int x)
{
siz[x]=siz[ls]+siz[rs]+isiz[x]+1;
int tmx=max(val[x],max(val[mx[ls]],val[mx[rs]]));
if(val[x]==tmx) mx[x]=x;
else if(val[mx[ls]]==tmx) mx[x]=mx[ls];
else mx[x]=mx[rs];
}
void pushdown(int x)
{
if(!rev[x]) return;
swap(ls,rs);rev[ls]^=1;rev[rs]^=1;rev[x]=0;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=get(x);
if(!isroot(y)) ch[z][get(y)]=x;
fa[ch[x][!k]]=y;fa[y]=x;fa[x]=z;
ch[y][k]=ch[x][!k];ch[x][!k]=y;
pushup(y);pushup(x);
}
void splay(int x)
{
st[top=1]=x;for(int t=x;!isroot(t);t=fa[t])st[++top]=fa[t];
while(top) pushdown(st[top--]);
while(!isroot(x))
{
int y=fa[x];
if(!isroot(y)) rotate(get(y)==get(x)?y:x);
rotate(x);
}
}
void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),isiz[x]+=siz[rs]-siz[t],rs=t,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
void getedge(int x,int y){makeroot(x);access(y);splay(y);}
void link(int i)
{
int x=e[i].u,y=e[i].v;makeroot(x);makeroot(y);
fa[x]=n+i;isiz[n+i]+=siz[x];pushup(n+i);
fa[n+i]=y;isiz[y]+=siz[n+i];pushup(y);
}
void cut(int i)
{
int x=e[i].u,y=e[i].v;getedge(x,y);
fa[x]=rs=ch[y][0]=0;
fa[n+i]=ch[n+i][0]=ch[n+i][1]=isiz[n+i]=0;pushup(n+i);
pushup(x);pushup(y);
}
int findf(int x){access(x);splay(x);while(ls)x=ls;return x;}
int odd(int x){return (siz[x]+1)>>1&1;}
#undef ls
#undef rs
}T;
}
using namespace Data_Structure;
namespace DreamLolita
{
int m,cnt;
void solution()
{
n=read();m=read();cnt=n;
for(int i=1;i<=n;++i) T.val[i]=-inf,T.pushup(i);
for(int i=1;i<=m;++i)
{
int x=read(),y=read(),z=read();
e[i].u=x;e[i].v=y;T.val[n+i]=z;T.mx[n+i]=n+i;
if(T.findf(x)==T.findf(y))
{
T.getedge(x,y);int id=T.mx[y]-n,w=T.val[T.mx[y]];
if(z<w){T.cut(id),T.link(i),Q.erase(data(w,id));}
else {if(cnt)puts("-1");else writeln(Q.top().val);continue;}
}
else
{
T.makeroot(x);cnt-=T.odd(x);T.makeroot(y);cnt-=T.odd(y);
T.link(i);cnt+=T.odd(y);
}
Q.insert(data(T.val[i+n],i));
if(cnt){puts("-1");continue;}
for(;;)
{
int id=Q.top().id,u=e[id].u,v=e[id].v;
T.getedge(u,v);if(T.odd(u))break;
T.cut(id);Q.erase(Q.top());
}
writeln(Q.top().val);
}
}
}
int main()
{
#ifdef Durant_Lee
freopen("CF603E.in","r",stdin);
freopen("CF603E.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}