CF603E Pastoral Oddities 优先队列+结论+LCT维护生成树
首先,一个神奇的结论:
一个合法的方案存在的条件是每一个联通块的节点数都是偶数个的.
这个可以用数学归纳法简单证一证.
证出这个后,我们只需动态加入每一个边,并查看一下有哪些边能够被删除(删掉后联通块依然合法).
对于维护加边,删边,我们用动态树.
对于枚举哪些边可以被删,我们可以用堆/set来维护.
由于每一条边最多只会加一次,也最多只会删一次,所以总时间复杂度为 $O(nlogm)$.
#include <cstdio> #include <queue> #include <cstring> #include <algorithm> #define lson t[x].ch[0] #define rson t[x].ch[1] #define N 500000 #define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout) using namespace std; struct Edge { int u,v,c,id; Edge(int u=0,int v=0,int c=0,int id=0):u(u),v(v),c(c),id(id){} bool operator<(Edge a) const{ return a.c>c; } }e[N]; priority_queue<Edge>q; int sta[N],n,m,del[N]; struct Node { int ch[2],max,val,son,size,f,id,rev; }t[N]; int isrt(int x) { return !(t[t[x].f].ch[0]==x||t[t[x].f].ch[1]==x); } int get(int x) { return t[t[x].f].ch[1]==x; } void mark(int x) { if(!x) return; swap(lson,rson), t[x].rev^=1; } void pushup(int x) { t[x].max=t[x].val,t[x].id=x; t[x].max=max(t[x].max,max(t[lson].max,t[rson].max)); if(t[lson].max==t[x].max) t[x].id=t[lson].id; if(t[rson].max==t[x].max) t[x].id=t[rson].id; t[x].size=t[x].son+t[lson].size+t[rson].size+(x<=n); } void pushdown(int x) { if(t[x].rev) mark(lson), mark(rson), t[x].rev=0; } void rotate(int x) { int old=t[x].f,fold=t[old].f,which=get(x); if(!isrt(old)) t[fold].ch[t[fold].ch[1]==old]=x; t[old].ch[which]=t[x].ch[which^1],t[t[old].ch[which]].f=old; t[x].ch[which^1]=old,t[old].f=x,t[x].f=fold; pushup(old),pushup(x); } void splay(int x) { int v=0,u=x,fa; for(sta[++v]=u;!isrt(u);u=t[u].f) sta[++v]=t[u].f; for(int i=v;i>=1;--i) pushdown(sta[i]); for(u=t[u].f;(fa=t[x].f)!=u;rotate(x)) if(t[fa].f!=u) rotate(get(fa)==get(x)?fa:x); } void Access(int x) { int y=0; while(x) { splay(x); t[x].son-=t[y].size; t[x].son+=t[rson].size; rson=y,pushup(x),y=x,x=t[x].f; } } void makeroot(int x) { Access(x),splay(x),mark(x); } int findroot(int x) { int u; Access(x),splay(x); while(x) { pushdown(x); u=x,x=lson; } return u; } void split(int x,int y) { makeroot(x),Access(y),splay(y); } void link(int x,int y) { makeroot(x), makeroot(y),t[x].f=y, t[y].son+=t[x].size,pushup(y); } void cut(int x,int y) { makeroot(x),Access(y),splay(y); t[y].ch[0]=t[x].f=0; pushup(y); } int main() { int i,j; // setIO("input"); scanf("%d%d",&n,&m); if(n%2==1) { for(i=1;i<=m;++i) printf("-1\n"); return 0; } int cnt=n; for(i=1;i<=m;++i) { int u,v,c; scanf("%d%d%d",&u,&v,&c); e[i]=Edge(u,v,c,i+n); int x=findroot(u),y=findroot(v); if(x!=y) { int now=i+n; makeroot(u),makeroot(v); if(t[u].size%2==1&&t[v].size%2==1) cnt-=2; t[now].val=c; link(u,now),link(now,v); q.push(Edge(u,v,c,now)); } else { split(u,v); if(t[v].max>c) { int cc=t[v].id,xx=e[cc-n].u,yy=e[cc-n].v,now=i+n; cut(cc,xx),cut(cc,yy), t[now].val=c; del[cc]=1; link(u,now),link(now,v); q.push(Edge(u,v,c,now)); } } if(cnt) printf("-1\n"); else { while(1) { while(!q.empty()&&del[q.top().id]) q.pop(); int xx=q.top().u,yy=q.top().v,cc=q.top().id,X,Y; makeroot(cc); Access(xx),splay(xx),X=t[xx].size-t[cc].size; Access(yy),splay(yy),Y=t[yy].size-t[cc].size; if(X%2==0&&Y%2==0) cut(xx,cc),cut(yy,cc),q.pop(); else break; } printf("%d\n",q.top().c); } } return 0; }