zoukankan      html  css  js  c++  java
  • CF603E Pastoral Oddities

    一、题目

    点此看题

    二、解法

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

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

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

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

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


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

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

    为了维护那些需要被提前加入的边,我们在递归下去的时候(偏序关系变化了)新加一些边即可,回溯的时候再回退掉多加入的边,所以使用可撤销并查集,时间复杂度 (O(nlog^2n)),但是比 ( t 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
    ",(!ans[i])?-1:e[ans[i]].c);
    }
    
  • 相关阅读:
    解决mac中wxpython对64位的支持
    python翻译词典实例
    php断点续传
    ubuntu配置telnet服务
    *p++,*++p,*(p++),*(++p)
    在main函数前后执行的函数之 C语言
    串行通讯协议--起止式异步通讯协议(UART)
    TTL电平, RS232电平以及CMOS电平的区别
    C 语言的关键字static 和C++ 的关键字static 有什么区别
    C语言各种数据类型取值范围
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15542920.html
Copyright © 2011-2022 走看看