JZOJ5275. 水管

11 篇文章 0 订阅
11 篇文章 0 订阅

这里写图片描述

题解

最小代价就一定是最小生成树,
这很显然。
但是这样只有40分,关键还是在判断方案是否唯一。

我们知道最小生成树有一个环切性质,
即在最小生成树中,添加一没有被选中的边,
那么就会构成一个环,而且这条新加进去的边一定是环上最大的边。

先证明一下上面的那个性质:
如果这个环上还有一条比这条新加入的边更大的边,
那我们就用这条新加入的边来替代比大的那条边,
这样我们得到的生成树更优,也就是说原来的树不是最小生成树,
与前提矛盾,所以这条新加进去的边一定是环上最大的边。

如果这环上面有一条与新加入的边相等的边,
那么用这条新加入的边来代替那条与它相等的边,
这样得到的也是最小生成树,原来的树,也是最小生成树,
那么最小生成树的情况就有多种。

我们枚举一条边(x,y)
通过倍增求出x->lca(x,y),y->lca(x,y)路径上的最大值,
与这条边(x,y)的权值作比较,
如果相等,最小生成树就有多种情况。

code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 200003
#define db double
#define P putchar
#define G getchar
#define mo 1000000007
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    int w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=n*10+ch-'0',ch=G();
    n*=w;
}

struct node
{
    int x,y,z;
}a[N*2];

bool cmp(node a,node b)
{
    return a.z<b.z;
}


int T,n,m,p,t,f1,f2,root;
int fa[N],f[N][17],g[N][17],deep[N];
int next[N*2],to[N*2],b[N],v[N*2],tot,z[N],top;
bool bz[2*N],vis[N];
ll ans;


int get(int x)
{
    if(fa[x]!=x)fa[x]=get(fa[x]);
    return fa[x];
}

void ins(int x,int y,int z)
{
    next[++tot]=b[x];
    to[tot]=y;
    v[tot]=z;
    b[x]=tot;
}

void dfs(int x)
{
    vis[x]=0;
    //printf("%d\n",deep[x]);
    for(int i=b[x];i;i=next[i])
        if(vis[to[i]])
        {
            f[to[i]][0]=x;
            deep[to[i]]=deep[x]+1;
            g[to[i]][0]=v[i];
            dfs(to[i]);
        }
}

int lca(int x,int y)
{
    int mx=0;
    if(deep[y]>deep[x])swap(x,y);
    for(int i=16;i>=0;i--)
        if(deep[f[x][i]]>=deep[y])mx=mx<g[x][i]?g[x][i]:mx,x=f[x][i];

    if(x==y)return mx;

    for(int i=16;i>=0;i--)
        if(f[x][i]!=f[y][i])mx=mx<g[x][i]?g[x][i]:mx,x=f[x][i],mx=mx<g[y][i]?g[y][i]:mx,y=f[y][i];

    mx=mx<g[x][0]?g[x][0]:mx,mx=mx<g[y][0]?g[y][0]:mx;
    return mx;
}
int main()
{
    read(T);
    while(T--)
    {
        read(n),read(m);
        for(int i=1;i<=m;i++)
            read(a[i].x),read(a[i].y),read(a[i].z);
        sort(a+1,a+1+m,cmp);
        for(int i=1;i<=n;i++)
            fa[i]=i;
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        memset(next,0,sizeof(next));
        memset(bz,1,sizeof(bz));

        p=ans=t=0;
        for(int i=1;t<n-1;i++)
        {
            f1=get(a[i].x);
            f2=get(a[i].y);
            if(f1!=f2)
            {
                ins(a[i].x,a[i].y,a[i].z);
                ins(a[i].y,a[i].x,a[i].z);
                fa[f2]=f1;
                ans+=a[i].z;
                t++;
                bz[i]=0;
            }
        }

        deep[1]=1;
        memset(vis,1,sizeof(vis));
        //dfs(n/2);
        z[1]=top=1;
        while(top)
        {
            int x=z[top];
            vis[x]=0;
            //printf("%d\n",deep[x]);
            while(!vis[to[b[x]]] && b[x]!=0)b[x]=next[b[x]];
            if(b[z[top]]==0)
            {
                top--;
                continue;
            }
            f[to[b[x]]][0]=x;
            deep[to[b[x]]]=deep[x]+1;
            g[to[b[x]]][0]=v[b[x]];
            //dfs(to[i]);
            z[++top]=to[b[x]];
            b[x]=next[b[x]];
        }
        for(int j=1;j<17;j++)
            for(int i=1;i<=n;i++)
                f[i][j]=f[f[i][j-1]][j-1],g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]);

        for(int i=1;i<=m;i++)
            if(bz[i])
            {
                if(lca(a[i].x,a[i].y)==a[i].z)
                {
                    p=1;
                    break;
                }
            }

        printf("%lld\n",ans);
        if(p==0)printf("Yes\n");else printf("No\n");

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值