2021.8.9提高B组模拟1T2 QYQ在艾泽拉斯(Tarjan强连通分量)(并查集)

Posted SSL_LKJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021.8.9提高B组模拟1T2 QYQ在艾泽拉斯(Tarjan强连通分量)(并查集)相关的知识,希望对你有一定的参考价值。

QYQ在艾泽拉斯

题目大意

在艾泽拉斯的无尽之海里,有着一群不为人知的由各个种族的冒险者统治的岛屿,这些岛屿都很庞大,足以在上面建造许多的城市,城市之间有一些单向道路连接。
有一天,QYQ无意中发现了这些岛屿,并且发现在每个城市的地下都或多或少埋藏着一些装备、金币、宝物……
可是正当QYQ兴奋不已打算全部把它们拿走时,他却惊奇的发现你的魔法在这里被限制住了,唯一可用的技能就是闪现,而且魔法只够他使用K次这个技能了,每次使用这个技能QYQ只能从一个岛屿上闪现到另外一个岛屿上。每一个岛屿只能登上一次,QYQ可以从任何一个城市开始旅程,在任何一个城市结束旅程。
城市的数量共有n个,有m条道路,每一条道路有两个参数u,v,表示从u到v有一条道路,但你只能由u到v走,两个城市属于相同的岛屿当且仅当暂时将所有道路视为双向道路时可以从其中一个城市走到另一个城市(可以途径其它城市)。
每一个城市都有一个宝物的总价值v[i],你的任务是帮助QYQ得到最大总价值的宝物,并输出这个值。

输入样例

从文件azeroth.in中输入数据。
输入的第一行包含两个整数n,m
输入的第二行到第m+1行,每行包含2个整数u,v,代表你可以从城市u走到城市v
输入的第m+2行包含n个整数,第i个整数代表v[i],即这个城市的宝物总价值。
输入的第m+3行包含一个整数K,代表你可以使用技能的次数。

3 2
1 2
3 1
1 2 1
0

输出样例

输出到文件azeroth.out中。
输出的第一行包含一个整数,代表QYQ能获得的最大的宝物总价值

4
样例说明:
QYQ从3号点开始,走到2号点,最后走到1号点,结束旅程,共获得1+2+1=4价值的宝物

题目数据

对于30%的数据:n<=10,K=0
对于50%的数据:n<=100,m<=100,K<=1
对于100%的数据:1<=n<=100000,1<=m<=1000000,1<=v[i]<=1000,0<=K<=100000
图中可能会有重边、自环。

解题思路

先用Tarjan强连通分量缩点

再用并查集求出每个岛屿最大的价值

我太蒟了,以至于不会敲这题的 dfs ,改了太久都没 AC ,只能用并查集,望各位大佬见谅

AC代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int n,m,k,T,TT,tot,tot1,top,answer; 
int f[100005],fa[100005],ru[100005],ans[100005],dfn[100005],low[100005],col[100005],sum[100005],num[100005],stak[100005],head[100005],head1[100005];
struct node
{
	int to,next;
}a[2000005],b[2000005];
void add(int x,int y)
{
	a[++tot]=(node){y,head[x]};
	head[x]=tot;
}
void add1(int x,int y)
{
	b[++tot1]=(node){y,head1[x]};
	head1[x]=tot1;
}
void Tarjan(int x)//模板
{
	dfn[x]=low[x]=++T;
	stak[++top]=x;
	for(int i=head[x];i;i=a[i].next)
	{
		int v=a[i].to;
		if(!dfn[v])
		{
			Tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else if(!col[v])low[x]=min(low[x],low[v]);
	}
	if(dfn[x]==low[x])
	{
		col[x]=++TT;
		sum[TT]+=num[x];
		while(stak[top]!=x)sum[TT]+=num[stak[top]],col[stak[top--]]=TT;
		top--;
	}
	return;
}
int getfather(int x)
{
	if(fa[x]==x)return x;
	return fa[x]=getfather(fa[x]);
}
void work(int x)
{
	f[x]+=sum[x];
	for(int i=head1[x];i;i=b[i].next)
	{
		getfather(x),getfather(b[i].to);
		fa[fa[x]]=fa[b[i].to];
		ru[b[i].to]--;
		f[b[i].to]=max(f[x],f[b[i].to]);
		if(!ru[b[i].to])work(b[i].to);
	}
}
bool cmp(int x,int y)
{
	return x>y;
}
int main()
{
	freopen("azeroth.in","r",stdin);
	freopen("azeroth.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	for(int i=1;i<=n;i++)
		 scanf("%d",&num[i]);
	scanf("%d",&k);
	for(int i=1;i<=n;i++)
		 if(!dfn[i])Tarjan(i);
	for(int i=1;i<=n;i++)//建新图
	 	for(int j=head[i];j;j=a[j].next)
	 	{
	 		int v=a[j].to;
	 		if(col[i]!=col[v])
		 	{
	 			add1(col[i],col[v]);
		 		ru[col[v]]++;
			}
		 }
	for(int i=1;i<=TT;i++)fa[i]=i;//初值
	for(int i=1;i<=TT;i++)if(!ru[i])work(i);//并查集
	for(int i=1;i<=TT;i++)getfather(i),ans[fa[i]]=max(ans[fa[i]],f[i]);//ans为各个岛屿最大价值
	sort(ans+1,ans+n+1,cmp);//排序
	for(int i=1;i<=min(n,k+1);i++)//求答案
		answer+=ans[i];
	printf("%d",answer);
	return 0;
}

谢谢

以上是关于2021.8.9提高B组模拟1T2 QYQ在艾泽拉斯(Tarjan强连通分量)(并查集)的主要内容,如果未能解决你的问题,请参考以下文章

2021.7.12提高B组模拟1T2 最短路径(DP)

2021.8.9提高B组模拟1T3 平均数(二分)(贪心)

2021.8.9提高B组模拟1T1 最长公共回文子序列(dfs)

2021.8.9提高B组模拟1T4 着色(组合数学)

2021.8.9提高B组模拟1T4 着色(组合数学)

2021.8.9提高B组模拟1T3 平均数(二分)(贪心)