CF603E Pastoral Oddities

一、题目

点此看题

二、解法

挺开心的,这道题基本上是自己想出来的 \(\tt ovo\)

首先有一个基本的 \(\tt observation\):翻转一条路径的选取情况,可以只改变路径端点的度数奇偶性。所以问题转化成把这些点两两配对使得每对之间都联通,显然它的充要条件是每个连通块大小都为偶数

上述结论还有另一种导出方式,因为选取一条边对度数的贡献是 \(2\),所以原问题的必要条件是每个连通块大小为偶数。然后考虑在这个基础上给出构造,考虑原图的 \(\tt dfs\) 树,可以从叶子开始考虑,如果它的度数为偶数就保留父边。最后除了根的所有点度数都是奇数,而总度数贡献是偶数,所以根的度数也是奇数。

考虑加边不会让合法性变差,因为本题要求最小化最大边权,而合法性关于边是有单调性的,所以我们可以尝试使用均摊法。每次新加边的时候考虑替换环上的最大边(类似最小生成树动态维护),输出答案之前尝试删除现有的最大边。

可以用 \(\tt lct\) 维护这个过程,需要用到维护虚子树信息的技巧(在 \(\tt access/link\) 虚实切换的时候修改),因为每条边最多被删除一次所以时间复杂度 \(O(n\log n)\)


还有一种方法是因为答案单调可以考虑决策性单调分治,我们考虑不进行删除操作使用并查集维护。

在计算答案在 \([x,y]\) 的区间 \([l,r]\),我们需要保证位置 \(<l\) 并且权值 \(<x\) 的边都已经被加入,这样在计算 \(mid\) 的答案时可以先暴力加入位置 \(\leq mid\) 并且权值 \(<x\) 的边,然后按权值大小加入位置 \(\leq mid\) 的边,当第一次合法的时候就得到了答案。

为了维护那些需要被提前加入的边,我们在递归下去的时候(偏序关系变化了)新加一些边即可,回溯的时候再回退掉多加入的边,所以使用可撤销并查集,时间复杂度 \(O(n\log^2n)\),但是比 \(\tt lct\) 快了一倍。

//link-cut-tree
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
const int M = 400005;
#define ls ch[x][0]
#define rs ch[x][1]
#define pii pair<int,int>
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x<0) putchar('-'),x=-x;
	if(x<=9)
	{
		putchar(x+'0');
		return ;
	}
	write(x/10);
	putchar(x%10+'0');
}
int n,m,ch[M][2],fa[M],fl[M],siz[M],ss[M],pos[M];
int cnt,a[M],b[M],c[M],st[M],out[M];priority_queue<pii> q;
int nrt(int x)
{
	return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
int chk(int x)
{
	return ch[fa[x]][1]==x;
}
void flip(int x)
{
	if(!x) return ;
	swap(ch[x][0],ch[x][1]);fl[x]^=1;
}
void up(int x)
{
	siz[x]=siz[ls]+siz[rs]+(x<=n)+ss[x];
	int tmp=c[pos[ls]]>c[pos[rs]]?pos[ls]:pos[rs];
	pos[x]=tmp;if(x>n) pos[x]=c[tmp]>c[x-n]?tmp:x-n;
}
void down(int x)
{
	if(!fl[x]) return ;
	flip(ls);flip(rs);fl[x]=0;
}
void rotate(int x)
{
	int y=fa[x],z=fa[y],k=chk(x),w=ch[x][k^1];
	ch[y][k]=w;fa[w]=y;
	if(nrt(y)) ch[z][chk(y)]=x;fa[x]=z;
	ch[x][k^1]=y;fa[y]=x;
	up(y);up(x);
}
void splay(int x)
{
	int y=x,z=0;st[++z]=y;
	while(nrt(y)) st[++z]=y=fa[y];
	while(z) down(st[z--]);
	while(nrt(x))
	{
		y=fa[x];
		if(nrt(y))
		{
			if(chk(x)==chk(y)) rotate(y);
			else rotate(x);
		}
		rotate(x);
	}
}
void access(int x)
{
	for(int y=0;x;x=fa[y=x])
	{
		splay(x);ss[x]+=siz[rs];
		ss[x]-=siz[rs=y];up(x);
	}
}
void makert(int x)
{
	access(x);splay(x);flip(x);
}
int findrt(int x)
{
	access(x);splay(x);
	while(ls) down(x),x=ls;
	splay(x);return x;
}
void split(int x,int y)
{
	makert(x);access(y);splay(y);
}
void link(int x,int y)
{
	makert(x);makert(y);
	cnt-=siz[x]&1;cnt-=siz[y]&1;
	fa[x]=y;ss[y]+=siz[x];up(y);
	cnt+=siz[y]&1; 
}
void cut(int x,int y)
{
	split(x,y);cnt-=siz[y]&1;
	ch[y][0]=fa[x]=0;up(y);
	cnt+=siz[x]&1;cnt+=siz[y]&1;
}
int zxy(int i)
{
	int x=a[i],y=b[i],z=c[i],ok=1;
	if(findrt(x)==findrt(y))
	{
		split(x,y);
		int o=pos[y];
		if(c[o]>z)
		{
			cut(a[o],o+n);
			cut(b[o],o+n);
			out[o]=1;
		}
		else ok=0;
	}
	if(ok)
	{
		pos[i+n]=i;
		link(x,i+n);link(y,i+n);
		q.push(make_pair(z,i));
	}
	if(cnt) return -1;
	while(!q.empty())
	{
		int o=q.top().second;q.pop();
		if(out[o]) continue;
		cut(a[o],o+n);cut(b[o],o+n);
		if(cnt)//become illegal
		{
			link(a[o],o+n),link(b[o],o+n);
			q.push(make_pair(c[o],o));
			return c[o];
		}
	}
	return 0;
}
signed main()
{
	cnt=n=read();m=read();
	for(int i=1;i<=n;i++) siz[i]=1;
	for(int i=1;i<=m;i++)
	{
		a[i]=read();b[i]=read();c[i]=read();
		write(zxy(i)),puts("");
	}
}
//divide and conquer optimize
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 300005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans[M],rk[M],cnt,top,fa[M],siz[M],st[M];
struct node
{
	int u,v,c,id;
	bool operator < (const node &b) const
	{
		return c<b.c;
	}
}e[M],E[M];
int find(int x)
{
	return x==fa[x]?x:find(fa[x]);
}
void add(int u,int v)
{
	int x=find(u),y=find(v);
	if(x==y) return ;
	cnt-=siz[x]&1;cnt-=siz[y]&1;
	if(siz[x]>siz[y]) swap(x,y);
	fa[x]=y;siz[y]+=siz[x];
	cnt+=siz[y]&1;st[++top]=x;
}
void back()
{
	int x=st[top--],y=fa[x];
	cnt-=siz[y]&1;
	fa[x]=x;siz[y]-=siz[x];
	cnt+=siz[x]&1;cnt+=siz[y]&1;
}
void cdq(int l,int r,int x,int y)
{
	if(l>r) return ;
	int mid=(l+r)>>1,zxy=top;
	for(int i=l;i<=mid;i++)
		if(rk[i]<x) add(E[i].u,E[i].v);
	for(int i=x;i<=y;i++)
	{
		if(e[i].id<=mid) add(e[i].u,e[i].v);
		if(!cnt) {ans[mid]=i;break;}
	}
	while(top>zxy) back();
	if(!ans[mid])
	{
		for(int i=l;i<=mid;i++)
			if(rk[i]<x) add(E[i].u,E[i].v);
		cdq(mid+1,r,x,y);
		while(top>zxy) back();
		return ;
	}
	for(int i=l;i<=mid;i++)
		if(rk[i]<x) add(E[i].u,E[i].v);
	cdq(mid+1,r,x,ans[mid]);
	while(top>zxy) back();
	for(int i=x;i<ans[mid];i++)
		if(e[i].id<l) add(e[i].u,e[i].v);
	cdq(l,mid-1,ans[mid],y);
	while(top>zxy) back();
}
signed main()
{
	cnt=n=read();m=read();
	for(int i=1;i<=n;i++) siz[i]=1,fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		e[i].u=read(),e[i].v=read(),e[i].c=read();
		e[i].id=i;E[i]=e[i];
	}
	sort(e+1,e+1+m);
	for(int i=1;i<=m;i++) rk[e[i].id]=i;
	cdq(1,m,1,m);
	for(int i=1;i<=m;i++)
		printf("%d\n",(!ans[i])?-1:e[ans[i]].c);
}
posted @ 2021-11-12 09:09  C202044zxy  阅读(133)  评论(0编辑  收藏  举报