Codeforces Round #234 (Div. 2) D. Dima and Bacteria(并查集划块缩点floyed最短路)

题意:

      有k种细菌,每种细菌c[i](i:1~k)个,总共n个,n个点之间有m条边,

第一个问题,相同种类的细菌之间能不能有一条花费为零的路。第二个问题,

在所有种类细菌均满足第一问条件的情况下,各个种类细菌之间的最短路。

思路:

      相邻两点之间花费为零,必然连接他们的边权为零。所以很容易想到把所有

边权为零的边保留,然后看同种细菌是不是在一个联通块中即可。 并查集、dfs

理论上应该都行(dfs可能数据量大点,可以试一下,只写了并查集)。

      然后各种类间最短路数据量较小,跑个弗洛伊德即可。

PS:两点间会有重边,inf的使用、防止爆精度。

AC代码:

#define LL long long
#define par pair<int,int>
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e5+100;
const int M=1e5+100;
int c[N];
int fat[N];
int find(int x)
{
    if(x==fat[x])return x;
    return fat[x]=find(fat[x]);
}
void unionn(int x,int y)
{
    int fa=find(x),fb=find(y);
    if(fa!=fb)fat[fa]=fb;
}
struct ee
{
    int l,r,v;
}e[M];
LL f[600][600];
int cnt[N];
int main()
{
    int n,m,k;
    while(cin>>n>>m>>k)
    {

        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<=n;++i)fat[i]=i;
        memset(c,0,sizeof(c));
        for(int i=1;i<=k;++i)
        {
            f[i][i]=0;
            for(int j=i+1;j<=k;++j)f[i][j]=f[j][i]=INF;
        }
        for(int i=1;i<=k;++i)cin>>c[i];
        for(int i=1;i<=m;++i)
        {
            int x,y,z;
            cin>>x>>y>>z;
            if(z==0)
            {
                unionn(x,y);
            }
            e[i].l=x;
            e[i].r=y;
            e[i].v=z;
        }
        int sum=0;
        bool flag=0;
        for(int i=1;i<=k;++i)
        {
            int tmp=find(sum+1);
            cnt[sum+1]=i;
            for(int j=sum+2;j<=sum+c[i];++j)
            {
                cnt[j]=i;
                if(tmp!=find(j))
                {
                    flag=1;
                    break;
                }
            }
            if(flag)break;
            sum+=c[i];
        }
        if(flag)
        {
            cout<<"No"<<endl;
            return 0;
        }
        else cout<<"Yes"<<endl;
        for(int i=1;i<=m;++i)
        {
            int x=e[i].l;
            int y=e[i].r;
            int z=e[i].v;
            if(cnt[x]==cnt[y])continue;
            else
            {
                if(z<f[cnt[x]][cnt[y]])f[cnt[x]][cnt[y]]=f[cnt[y]][cnt[x]]=z;//*****
            }

        }
        for(int kk=1;kk<=k;++kk)
            for(int i=1;i<=k;++i)
                for(int j=1;j<=k;++j)
                {
                    if(f[i][kk]==INF||f[kk][j]==INF)continue;
                    if(f[i][kk]+f[kk][j]<f[i][j])f[i][j]=f[i][kk]+f[kk][j];
                }
        for(int i=1;i<=k;++i)
        {
            for(int j=1;j<=k;++j)
            {
                if(f[i][j]>=INF)f[i][j]=-1;
                cout<<f[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    return 0;
}

 

The end;

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值