CF603E Pastoral Oddities

Link
分析发现合法的条件为每个连通块的大小为偶数,考虑用带撤销并查集维护。
一个暴力是从小往大加边,直到所有连通块大小都为偶数。
线段树分治优化即可。

#include<cctype>
#include<cstdio>
#include<cstring>
#include<numeric>
#include<utility>
#include<algorithm>
using pi=std::pair<int,int>;
const int N=300007;
int n,m,top,odd,ans[N],p[N],rank[N],fa[N],size[N];pi stk[N];struct edeg{int u,v,w;}e[N];
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int find(int x){return x==fa[x]? x:find(fa[x]);}
void merge(int u,int v)
{
    if((u=find(u))==(v=find(v))) return ;
    if(size[u]>size[v]) std::swap(u,v);
    if(size[u]&1&&size[v]&1) odd-=2;
    fa[u]=v,size[v]+=size[u],stk[++top]={u,v};
}
void undo(int pos){for(int u,v;top>pos;--top)if(u=stk[top].first,v=stk[top].second,fa[u]=u,size[v]-=size[u],size[u]&1&&size[v]&1)odd+=2;}
void solve(int l,int r,int L,int R)
{
    if(l>r) return;
    int t=top,mid=(l+r)>>1,pos=-1;
    for(int i=l;i<=mid;++i) if(rank[i]<L) merge(e[i].u,e[i].v);
    for(int i=L;i<=R&&odd;++i)
    {
	if(p[i]<=mid) merge(e[p[i]].u,e[p[i]].v);
	if(!odd) {pos=i;break;}
    }
    undo(t);
    if(!~pos)
    {
	memset(ans+l,-1,(mid-l+1)<<2);
	for(int i=l;i<=mid;++i) if(rank[i]<L) merge(e[i].u,e[i].v);
	return solve(mid+1,r,L,R),undo(t);
    }
    ans[mid]=e[p[pos]].w;
    for(int i=L;i<pos;++i) if(p[i]<l) merge(e[p[i]].u,e[p[i]].v);
    solve(l,mid-1,pos,R),undo(t);
    for(int i=l;i<=mid;++i) if(rank[i]<L) merge(e[i].u,e[i].v);
    solve(mid+1,r,L,pos),undo(t);
}
int main()
{
    n=read(),m=read(),odd=n,std::fill(size+1,size+n+1,1),std::iota(p+1,p+m+1,1),std::iota(fa+1,fa+n+1,1);
    for(int i=1;i<=m;++i) e[i]={read(),read(),read()};
    std::sort(p+1,p+m+1,[](int i,int j){return e[i].w<e[j].w;});
    for(int i=1;i<=m;++i) rank[p[i]]=i;
    solve(1,m,1,m);
    for(int i=1;i<=m;++i) printf("%d
",ans[i]);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12748097.html