CodeForces - 1408E Avoid Rainbow Cycles(思维+最大生成树)

题目链接:点击查看

题目大意:给出 m 个集合,每个集合中都有数个点,每个点的取值范围为 [ 1 , n ] ,对于每个集合而言,其中的点互相连边,边的颜色为其集合的编号,每次操作可以删除点,删除掉第 i 个集合中的点 j 的代价为 a[ i ] + b[ j ] ,现在规定 “彩虹环” 的定义为,一条环上的所有边的编号互不相同,现在问最少花费多少边权进行删点才能使得图中不存在 “彩虹环”

题目分析:模型的话很像之前训练的时候做过的一个最短路,也是好多个集合,每个集合中的点互相连边:HDU - 5521

于是不难想到对于每个集合而言,建立一个虚拟节点来表示此集合中的点两两可达,又因为题目中给出了删除掉每个节点的权值,所以虚拟节点与每个节点相连后的边权就是相应的 a[ i ] + b[ j ] 了

现在考虑转换最终的问题,也就是不存在 “彩虹环” 代表的到底是什么,因为经过了上一段的建模后,新的模型的特点是:

  1. 每个集合中的任意两点都是可达的
  2. 每个集合中不存在环

这样在新图中,如果存在环的话,那么一定是来自于不同集合的边所造成的贡献,所以我们只需要进行删边,使得新图中不存在环即可

因为要求删除的边权最小,换个角度就是剩下的树的权值尽可能大,这样题目就转换成了最大生成树的问题了

代码:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map> 
using namespace std;
      
typedef long long LL;
      
typedef unsigned long long ull;
 
const int inf=0x3f3f3f3f;

const int N=2e5+100;

struct Edge
{
	int u,v;
	LL w;
	bool operator<(const Edge& t)const
	{
		return w>t.w;
	}
}edge[N];

int cnt=0;

LL a[N],b[N];

int f[N<<1];

int find(int x)
{
	return x==f[x]?x:f[x]=find(f[x]);
}

bool merge(int x,int y)
{
	int xx=find(x),yy=find(y);
	if(xx!=yy)
	{
		f[xx]=yy;
		return true;
	}
	return false;
}

void init()
{
	for(int i=0;i<N<<1;i++)
		f[i]=i;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	init();
	int m,n;
	LL ans=0;
	scanf("%d%d",&m,&n);
	for(int i=1;i<=m;i++)
		scanf("%lld",a+i);
	for(int i=1;i<=n;i++)
		scanf("%lld",b+i);
	for(int i=1;i<=m;i++)
	{
		int num;
		scanf("%d",&num);
		while(num--)
		{
			int x;
			scanf("%d",&x);
			cnt++;
			edge[cnt].u=i+n;
			edge[cnt].v=x;
			edge[cnt].w=a[i]+b[x];
			ans+=a[i]+b[x];
		}
	}
	sort(edge+1,edge+1+cnt);
	for(int i=1;i<=cnt;i++)
		if(merge(edge[i].u,edge[i].v))
			ans-=edge[i].w;
	printf("%lld\n",ans);
























   return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108908707
今日推荐