E.Avoid Rainbow Cycles
题意:
有m个集合,n个点,每个集合有一些1到n的数,这些数代表的点构成一个完全图,边的颜色为i(第i个集合),然后定义彩虹环是一个环,这个环上的所有边都是不同颜色的,每次操作可以删除一个集合中的一个点,花费为a[i]+b[j],代表第i个集合的第j点,求最小花费使得最终不存在彩虹环。
思路:
这个题很难下手,因为彩虹环定义和完全图这些东西太绕了,但是其实可以定义一个点n+i,连着第i集合中的所有点,这样其实也就成了完全图,就抽象为了某个点,经过第n+i个点(即i颜色的边)到达另一个点,这个和克鲁斯重构树差不多,把边化为点,然后这样的话最终一定得是个树才行,否则只要有环,则一定是存在彩虹环,因为点和点之间没有任何连边,所以这个图一定是点-边-点-边这种的形式,所以出现了环,则某个点一定是连接了至少两个不同颜色边,因此肯定出现了彩虹环,因此题目就可以转化为求最小花费使得这个图变成一棵树,这样就可以变成了最大生成树问题。
总结:如果碰到了这种的完全图问题,可以建立一个总点,这样就可以简化图了,从而使得模型更加清晰简约,这个题稍微有些重构树的思想,这种模型确实很神奇。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_N=101000;
long long a[MAX_N],b[MAX_N];
struct skt{
int x,y;
long long z;
}edge[2*MAX_N];
int pre[2*MAX_N];
bool cmp(skt a,skt b){
return a.z>b.z;
}
int find(int x){
if(pre[x]!=x)
pre[x]=find(pre[x]);
return pre[x];
}
int main(void){
int n,m,i,j,k;
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++)
scanf("%lld",&a[i]);
for(i=1;i<=n;i++)
scanf("%lld",&b[i]);
for(i=1;i<=n+m;i++)
pre[i]=i;
int cnt=0;
long long res=0;
for(i=1;i<=m;i++){
int x;
scanf("%d",&k);
for(j=1;j<=k;j++){
scanf("%d",&x);
edge[++cnt].x=n+i,edge[cnt].y=x,edge[cnt].z=a[i]+b[x];
res+=edge[cnt].z;
}
}
sort(edge+1,edge+cnt+1,cmp);
for(i=1;i<=cnt;i++){
int x=edge[i].x,y=edge[i].y;
long long z=edge[i].z;
int fx=find(x),fy=find(y);
if(fx!=fy){
pre[fx]=fy;
res-=z;
}
}
printf("%lld\n",res);
return 0;
}