【WC2006】水管局长

问题描述

SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。
在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于MY市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。
不妨将MY市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。

输入格式

第一行为3个整数:N, M, Q分别表示管道连接处(结点)的数目、目前水管(无向边)的数目,以及你的程序需要处理的任务数目(包括寻找一条满足要求的路径和接受某条水管坏掉的事实)。
以下M行,每行3个整数x, y和t,描述一条对应的水管。x和y表示水管两端结点的编号,t表示准备送水所需要的时间。我们不妨为结点从1至N编号,这样所有的x和y都在范围[1, N]内。
以下Q行,每行描述一项任务。其中第一个整数为k:若k=1则后跟两个整数A和B,表示你需要为供水公司寻找一条满足要求的从A到B的水管路径;若k=2,则后跟两个整数x和y,表示直接连接x和y的水管宣布报废(保证合法,即在此之前直接连接x和y尚未报废的水管一定存在)。

输出格式

每一项k=1的任务,你需要输出一个数字和一个回车/换行符。
该数字表示:你寻找到的水管路径中所有管道全都完成准备工作所需要的时间(当然要求最短)。

样例输入

4 4 3
1 2 2
2 3 3
3 4 2
1 4 2
1 1 4
2 1 4
1 1 4

样例输出

2
3

数据规模

N ≤ 1000
M ≤ 100000
Q ≤ 100000
测试数据中宣布报废的水管不超过5000条;且任何时候我们考虑的水管网络都是连通的,即从任一结点A必有至少一条水管路径通往任一结点B。


题解

算劲题吗?好像算。。。
//先说一句,下面的描述中“水管”和“边”是一个东西。。。
看一眼题目,让最小值最大,显然二分,让路径上的最大值最小,显然想到最小生成树(贪心一下即可)。跑一遍最小生成树,两点之间的路径极大值即为答案。
恶心的是删边问题。显然不能在上一次讨论的基础上接着讨论(这个“显然”要感谢PhantasmDragon大佬)。但如果暴力搞,一次时间复杂度就是O(m)。结果本蒟蒻受了数据的蛊惑(测试数据中宣布报废的水管不超过5000条),一直在想如何卡常。。。。。
后来想能否找到快速查出俩连通块之间的最短边。想啊想。。。
最后,终于想起之前做的假期关楼(NKOJ3696),想起了可爱的猴子(NKOJ2107),反向考虑。
我们不妨将水管挂掉的事实反过来,从后往前讨论,看作新水管出现。我们在没有这些水管的情况下先跑一遍最小生成树,每“出现”一根水管,就先算出这根水管两端点的路径极大值。如果极大值小于这根水管的值,那么它没用;否则,(根据贪心,),删掉原路径上值最大的边,将此边加入。
维护动态树,用LCT。嗯 。
思路就是这样。但还有几个注意事项。首先,离线处理吧,这个显然。所以有很多东西你需要记录。其次,处理边权的问题。把边权转为儿子节点的点权的方法会G,因为根在变。。。推荐老板的方法:在两端点之间再加一个点,这个点的点权即为原来的边权,其余点边权为0 。还有,注意你要删边,所以记录极大值时还要记录位置。。。最后,要仔细,本蒟蒻把2打成1调了一个半小时QaQ。


代码

鸣谢 机智的何老板
鸣谢 高深莫测的何老板

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <stack>
using namespace std;
const int Q=1210005;
struct dt{
	int x,y,v;
	bool ch;
}e[Q];
bool operator<(dt a,dt b)
{return a.x==b.x?a.y<b.y:a.x<b.x;}
bool cmp(dt a,dt b)
{return a.v<b.v;}
int tot,qu[4][Q],o[3][Q],ls[Q],rs[Q],v[Q],f[Q],an[Q],maxn[Q],lazy[Q],n,ans[Q];
//注意了!!!maxn[] 存的是编号!!!
int fa[Q];
int gf(int x)
{
	if(fa[x]!=x)fa[x]=gf(fa[x]);
	return fa[x];
}
int gm(int x,int y)
{v[0]=0;return v[maxn[x]]>v[maxn[y]]?maxn[x]:maxn[y];}
void update(int x)
{
	maxn[x]=gm(ls[x],rs[x]);
	if(v[x]>v[maxn[x]])maxn[x]=x;
}
void lx(int x)
{
	int y=f[x],z=f[y];
	if(z)if(ls[z]==y)ls[z]=x;
	else rs[z]=x;
	f[x]=z;
	rs[y]=ls[x];
	f[rs[y]]=y;
	f[y]=x;
	ls[x]=y;
	swap(an[x],an[y]);
	maxn[x]=maxn[y];
	update(y);
}
void rx(int x)
{
	int y=f[x],z=f[y];
	if(z)if(ls[z]==y)ls[z]=x;
	else rs[z]=x;
	f[x]=z;
	ls[y]=rs[x];
	f[ls[y]]=y;
	f[y]=x;
	rs[x]=y;
	swap(an[x],an[y]);
	maxn[x]=maxn[y];
	update(y);
}
void pd(int x)
{
	if(!lazy[x])return;
	lazy[x]=0;
	swap(ls[x],rs[x]);
	if(ls[x])lazy[ls[x]]^=1;
	if(rs[x])lazy[rs[x]]^=1;
}
int st[Q];
void splay(int x)
{
	int toap=0;
	for(int t=x;t;t=f[t])st[++toap]=t;
	while(toap)pd(st[toap--]);
	while(f[x])
	{
		int y=f[x],z=f[y];
		if(z)if(ls[z]==y)
			if(ls[y]==x)rx(y),rx(x);
			else lx(x),rx(x);
		else if(rs[y]==x)lx(y),lx(x);
			else rx(x),lx(x);
		else if(ls[y]==x)rx(x);
			else lx(x);
	}
}
void ac(int x)
{
	int y=0;
	while(x)
	{
		splay(x);
		if(rs[x])f[rs[x]]=0,an[rs[x]]=x;
		rs[x]=y;
		if(y)f[y]=x;
		update(x);
		y=x,x=an[x];
	}
}
void mr(int x)
{
	ac(x);
	splay(x);
	lazy[x]^=1;
}
void link(int x,int y)
{
	mr(x);
	an[x]=y;
}
void cut(int x,int y)
{
	mr(x);
	ac(y);
	splay(y);
	pd(y);
	an[y]=an[x]=ls[y]=f[x]=0;
	update(x),update(y);
}
int main()
{
	int m,q,cnt=0;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++)maxn[i]=fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
		if(e[i].x>e[i].y)swap(e[i].x,e[i].y);
		e[i].ch=false;
	}
	sort(e+1,e+m+1);
	for(int i=1;i<=q;i++){
		scanf("%d%d%d",&qu[0][i],&qu[1][i],&qu[2][i]);
		if(qu[1][i]>qu[2][i])swap(qu[1][i],qu[2][i]);
		if(qu[0][i]==2)
		{
			int t=lower_bound(e+1,e+m+1,(dt){qu[1][i],qu[2][i],0,false})-e;
			e[t].ch=true;
			qu[3][i]=e[t].v;
		}
	}
	tot=n;
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=m&&cnt<n-1;i++)
	{
		if(e[i].ch)continue;
		int fx=gf(e[i].x),fy=gf(e[i].y);
		if(fx==fy)continue;
		fa[fx]=fy;
		v[++tot]=e[i].v;
		maxn[tot]=tot;
		o[1][tot]=e[i].x,o[2][tot]=e[i].y;
		link(tot,e[i].x),link(tot,e[i].y);
		++cnt;
	}
	for(int i=q;i;--i)
	{
		int x=qu[1][i],y=qu[2][i];
		if(qu[0][i]==2)
		{
			ans[i]=-1;
			int now=qu[3][i];
			mr(x),ac(y);
			splay(x);
			int temp=maxn[x];
			if(v[temp]<=now)continue;
			cut(temp,o[1][temp]),cut(temp,o[2][temp]);
			v[++tot]=now;
			maxn[tot]=tot;
			o[1][tot]=x,o[2][tot]=y;
			link(tot,x),link(tot,y);
		}
		else{
			mr(x);
			ac(y);
			splay(x);
			ans[i]=v[maxn[x]];
		}
	}
	for(int i=1;i<=q;i++)
		if(ans[i]!=-1)
			printf("%d\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值