【noi.ac #596】road

题目

小P和小R是一对非常好的朋友,今天他们在玩一个模拟建设类游戏。

游戏中共有nn个城市,通过mm条双向道路连接。第ii条道路连接了城市aiai和bibi。

不幸的是,在一次巨大的灾难以后,这mm条道路全部损坏了。修复第ii条道路需要cici天。把这些道路全部修复的代价可能太大,小P和小R只希望某kk个城市之间两两恢复通行。

游戏中,小P和小R拥有很多的修路工人,所以如果一个修路方案包含多条道路,那么这些道路可以同时开工。整个工程完工的时间就是这个工程中需要时间最长的道路的用时。

小P和小R为了给你加大难度,一共要问你qq个这样的问题。不同的问题之间不会互相影响,你可以认为这qq个问题是发生在不同的平行世界中的。

输入格式
从标准输入读入数据。

第一行包含三个整数n,m,qn,m,q。

接下来mm行,每行三个整数ai,bi,ciai,bi,ci,描述一条道路。注意道路的两端有可能是相同的城市。

接下来qq行,每行描述一个问题:第一个数是这个问题的kk,接下来kk个数表示这次问题中需要两两恢复通行的城市编号。保证kk至少为1;一个问题中可能多次出现同一个城市。

输出格式
输出到标准输出。

输出qq行,依次表示每一个问题的答案。

如果不需要建设任何道路,输出0;如果无论如何也无法完成,输出INF。

样例
输入
5 6 3
1 2 4
2 3 4
3 1 4
1 4 3
2 4 3
3 4 3
3 1 2 3
4 1 2 3 5
2 5 5
输出
3
INF
0
解释
如果直接修复1到2和2到3的两条路,可使1,2,3三个城市两两恢复通行,需要4天。然而,如果修复1到4,2到4和3到4的三条路,只需3天就可以了。

由于5与其余城市都没有路,所以不可能使1,2,3,5四个城市恢复通行。然而,如果只需要5能到达自己,则不需要修复任何道路。

子任务
除样例外,所有的道路和问题都是随机生成的。

对于所有的数据,1≤ai,bi≤n,0≤ci≤1091≤ai,bi≤n,0≤ci≤109。

令∑k∑k表示某个输入数据当中所有问题的kk值的总和。

对于30%的数据,n≤120,m≤300,q≤300,∑k≤36000n≤120,m≤300,q≤300,∑k≤36000。
对于另外20%的数据,n≤400,m≤500000,q≤200000,∑k≤1000000n≤400,m≤500000,q≤200000,∑k≤1000000。
对于另外20%的数据,n≤2000,m≤4000,q≤200000,∑k≤1000000n≤2000,m≤4000,q≤200000,∑k≤1000000。
对于100%的数据,n≤200000,m≤500000,q≤200000,∑k≤1000000n≤200000,m≤500000,q≤200000,∑k≤1000000。
每一档分数的数据之间均有梯度,且均匀分布着一半的数据保证q≤10q≤10。

思路

考场上虚树写挂了,只有50

其实我们并不需要把虚树建出来,甚至不用dfs序,对于每个询问暴力求lca就行

这里给一个比较妙的做法:维护并查集同时维护最大值,代码量会少很多

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+77,inf=0x3f3f3f3f;
int fa[N],yjy[N],siz[N];
struct A
{
	int x,y,c;
}a[N];
bool operator <(const A x,const A y)
{
	return x.c<y.c;
}
int main()
{
	int n,m,q; 
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1; i<=m; i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c);
	for(int i=1; i<=n; i++)
	{
		fa[i]=i; siz[i]=1;
	}
	memset(yjy,0x3f,sizeof(yjy));
	sort(a+1,a+m+1);
	for(int i=1; i<=m; i++)
	{
		int x=a[i].x,y=a[i].y,w=a[i].c;
		while(yjy[x]!=inf) x=fa[x]; while(yjy[y]!=inf) y=fa[y];
		if(x!=y)
		{
			if(siz[x]<siz[y])
			{
				fa[x]=y; yjy[x]=w; siz[y]+=siz[x];
			}
			else
			{
				fa[y]=x; yjy[y]=w; siz[x]+=siz[y];
			}
		}
	}
	while(q--)
	{
		int k,c,ans=0;
		scanf("%d%d",&k,&c);
		while(--k)
		{
			int d; 
			scanf("%d",&d);
			int x=c,y=d;
			while(x!=y)
			{
				if(yjy[x]>yjy[y]) swap(x,y); ans=max(ans,yjy[x]);
				if(ans==inf) break;
				x=fa[x];
			}
			c=d;
		}
		if(ans<inf) printf("%d\n",ans); else printf("INF\n");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值