Codeforces 400D Dima And Bacteria 暴力+Floyd

题意:n点,m条带权边的无向图,每个顶点属于k类中的某一类,定义合法,同一类中的任意两点存在代价为0的路径,若合法,求出d[i][j]矩阵,表示第i类到第j类的最小代价.

n,m<=1e5,k<=500,wi<=1e4


同一个类有c[i]个点 则要有c[i]*(c[i]-1)/2条权值为0的路径.相同type转化时,不会经过非0边

首先把边权为0两个端点扔到同一个联通分量中,算出该联通分量中每个type的个数,更新该type路径个数即可判断是否合法.O(M) ...其实相同type必须在同一个联通分量中,否则没有代价0路径存在,用并查集即可.

若合法,则以type为定点,type间的最小代价为边建图(因为相同type间代价为0)

然后跑一遍即可 O(K^3)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int N=2e5+20;
const int M=5e2+20;
const ll inf=2e15;
vector<int> e[N];
ll d[M][M];
ll n,m,k,c[N],l[N],b[N],a[N];//l[i] type[i]'s left pos
int vis[N],id[N];
int get(int x)
{
	for(int i=k;i>=1;i--)
	{
		if(x>=l[i])
			return i;
	}	
}
void dfs(int u)
{
	vis[u]=1;
	a[id[u]]++;
	for(int i=0;i<e[u].size();i++)
		if(!vis[e[u][i]])
			dfs(e[u][i]);
}
bool check()
{
	memset(vis,0,sizeof(vis));
	memset(b,0,sizeof(b));
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			memset(a,0,sizeof(a));
			dfs(i);	
			for(int j=1;j<=k;j++)
				b[j]+=a[j]*(a[j]-1)/2;
		}
	}
	for(int i=1;i<=k;i++)
		if(b[i]!=c[i]*(c[i]-1)/2)
			return false;
	return true;
}
bool floyd()
{
	for(int p=1;p<=k;p++)
		for(int i=1;i<=k;i++)
			for(int j=1;j<=k;j++)
				d[i][j]=min(d[i][j],d[i][p]+d[p][j]);
	printf("Yes\n");
	for(int i=1;i<=k;i++)
	{
		if(c[i]==1)
			d[i][i]=0;
		for(int j=1;j<=k;j++)
		{
			if(d[i][j]>=inf)
				d[i][j]=-1;
			printf("%I64d%c",d[i][j],j==k?'\n':' ');
		}
	}
}
int main()
{
	while(cin>>n>>m>>k)
	{
		ll num=0,zero=0,u,v,w;
		l[1]=1;
		for(int i=1;i<=k;i++)
			scanf("%d",&c[i]),num+=c[i]-1,l[i+1]=l[i]+c[i];
		for(int i=1;i<=n;i++)
			id[i]=get(i),e[i].clear();	
		for(int i=1;i<=k;i++)
			for(int j=1;j<=k;j++)
				d[i][j]=inf;
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%I64d",&u,&v,&w);
			if(w==0)
				e[u].push_back(v),e[v].push_back(u);
			u=get(u),v=get(v);
			d[u][v]=min(d[u][v],w);
			d[v][u]=min(d[v][u],w);
		}
		if(check()==false)
		{
			puts("No");
			continue;
		}
		floyd();
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值