bzoj-2594 水管局长数据加强版

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

给出一个n个点m条边的无向图,边上有权值;

Q次询问,每次有两种操作:
1.求x,y两点路径上的最大值的最小值;

2.删除一条边;

保证删除的边存在,保证图时刻连通,保证不会出现重边和自环;

n≤100000,m≤1000000,Q≤100000;


题解:
这是一个动态图问题,但是由于询问操作的特殊性,我们也可以转化到树上做;

仔细看看不就是带删边的货车运输吗!

那么用LCT来维护最小生成树查询操作1就可以了;

但是LCT维护最小生成树是不能删边的;

考虑倒着处理询问,删边就成了加边;

加边会出现环,那么就将权值最大的边弹出去就好了;

弹边操作似乎比较恶心,那么将边转化成一个点,这个点连接两个点;

那么就把边权转化到点上,删除这个点的两条边就是了;

此题预处理比较糟糕(似乎离线处理的东西都这样);


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define Pt 330000
#define which(x) (ch[fa[x]][1]==x)
using namespace std;
struct node
{
	bool op;
	int x,y,val,no;
}Q[N];
struct edge
{
	int x,y,val;
	bool del;
	friend bool operator <(edge a,edge b)
	{
		if(a.x==b.x)
		return a.y<b.y;
		return a.x<b.x;
	}
}total[N*10],temp;
int fa[Pt],ch[Pt][2],val[Pt],ma[Pt],wma[Pt],ans[N],f[N];
bool rt[Pt],rev[Pt];
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')	ch=getchar();
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x;
}
int cmp(edge a,edge b)
{
	if(a.del==b.del)
	return a.val<b.val;
	return a.del<b.del;
}
void Pushup(int x)
{
	ma[x]=max(val[x],max(ma[ch[x][0]],ma[ch[x][1]]));
	wma[x]=ma[x]==val[x]?x:(ma[x]==val[wma[ch[x][0]]]?wma[ch[x][0]]:wma[ch[x][1]]);
}
void reverse(int x)
{
	swap(ch[x][0],ch[x][1]);
	rev[x]^=1;
}
void Pushdown(int x)
{
	if(rev[x])
	{
		reverse(ch[x][0]);
		reverse(ch[x][1]);
		rev[x]=0;
	}
}
void down(int x)
{
	if(!rt[x])	down(fa[x]);
	Pushdown(x);
}
void Rotate(int x)
{
	int f=fa[x];
	bool k=which(x);
	if(rt[f])	rt[f]^=rt[x]^=1;
	else		ch[fa[f]][which(f)]=x;
	ch[f][k]=ch[x][!k];
	ch[x][!k]=f;
	fa[ch[f][k]]=f;
	fa[x]=fa[f];
	fa[f]=x;
	Pushup(f);
	Pushup(x);
}
void Splay(int x)
{
	down(x);
	while(!rt[x])
	{
		int f=fa[x];
		if(rt[f])
		{
			Rotate(x);
			return ;
		}
		if(which(x)^which(f))
			Rotate(x);
		else
			Rotate(f);
		Rotate(x);
	}
}
void access(int x)
{
	int y=0;
	while(x)
	{
		Splay(x);
		rt[ch[x][1]]=1,rt[y]=0;
		ch[x][1]=y;
		Pushup(x);
		y=x,x=fa[x];
	}
}
void Mtr(int x)
{
	access(x);
	Splay(x);
	reverse(x);
}
void Link(int x,int y)
{
	Mtr(x);
	fa[x]=y;
}
void Cut(int t,int x,int y)
{
	Mtr(t);
	access(x);
	Splay(t);
	rt[ch[t][1]]=1;
	fa[ch[t][1]]=0;
	ch[t][1]=0;
	access(y);
	Splay(t);
	rt[ch[t][1]]=1;
	fa[ch[t][1]]=0;
	ch[t][1]=0;
	Pushup(t);
}
int get_max(int x,int y)
{
	Mtr(x);
	access(y);
	Splay(x);
	return ma[x];
}
int get_wmax(int x,int y)
{
	Mtr(x);
	access(y);
	Splay(x);
	return wma[x];
}
int find(int x)
{
	return f[x]==x?x:find(f[x]);
}
void Build_Min_Tree(int n,int m)
{
	sort(total+1,total+m+1,cmp);
	for(int i=1;i<=n;i++)
		f[i]=i;
	int cnt=0;
	for(int i=1;i<=m;i++)
	{
		int x=find(total[i].x),y=find(total[i].y);
		if(x!=y)
		{
			f[x]=y;
			swap(total[i],total[++cnt]);
			if(cnt==n-1)
				break;
		}
	}
}
int main()
{
	int n,m,q,i,j,k,cnt1,cnt2,op,x,y,v;
	scanf("%d%d%d",&n,&m,&q);
	for(i=1;i<=m;i++)
	{
		total[i].x=read(),total[i].y=read(),total[i].val=read();
		if(total[i].x>total[i].y)
			swap(total[i].x,total[i].y);
	}
	sort(total+1,total+1+m);
	for(i=1,cnt1=cnt2=0;i<=q;i++)
	{
		op=read();
		if(op==1)
			Q[i].x=read(),Q[i].y=read(),Q[i].no=++cnt1;
		else
		{
			Q[i].op=1;
			Q[i].x=read(),Q[i].y=read();
			if(Q[i].x>Q[i].y)	swap(Q[i].x,Q[i].y);
			temp.x=Q[i].x,temp.y=Q[i].y;
			k=lower_bound(total+1,total+m+1,temp)-total;
			total[k].del=1,Q[i].val=total[k].val;
			Q[i].no=++cnt2;
		}
	}
	Build_Min_Tree(n,m);
	for(i=1;i<=n;i++)
		rt[i]=1,wma[i]=i;
	for(i=1;i<n;i++)
	{
		rt[i+n]=1,wma[i+n]=i+n,val[i+n]=ma[i+n]=total[i].val;
		Link(i+n,total[i].x);
		Link(i+n,total[i].y);
	}
	for(i=q;i>=1;i--)
	{
		if(Q[i].op)
		{
			x=Q[i].x,y=Q[i].y;
			rt[Q[i].no+(n<<1)]=1,wma[Q[i].no+(n<<1)]=Q[i].no+(n<<1);
			val[Q[i].no+(n<<1)]=ma[Q[i].no+(n<<1)]=Q[i].val;
			k=get_wmax(x,y);
			if(val[k]>Q[i].val)
			{
				Cut(k,x,y);
				Link(Q[i].no+(n<<1),x);
				Link(Q[i].no+(n<<1),y);
			}
		}
		else
		{
			ans[Q[i].no]=get_max(Q[i].x,Q[i].y);
		}
	}
	for(i=1;i<=cnt1;i++)
	{
		printf("%d\n",ans[i]);
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值