【LCT】CF603E Pastoral Oddities

【题目】
添加链接描述
一幅 n n n个点的图,动态加入 m m m条边,每次加边后你需要找到一个最大边权最小的边集使得每个点的度数都是奇数,求这个最大边权。
n ≤ 1 0 5 , m ≤ 3 × 105 n\leq 10^5,m\leq 3\times 105 n105,m3×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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值