1 条题解

  • 0
    @ 2025-8-24 22:03:48

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Graphcity
    循此苦旅,终抵繁星。

    搬运于2025-08-24 22:03:48,当前版本为作者最后更新于2022-10-18 11:02:40,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    • 简要题意

    给定一张 nn 个点,mm 条边的带权无向图,其中前 n1n-1 条边为黑色并构成一棵树,其它边为白色。你需要给出一棵生成树,使得将其中一条边 (u,v,w)(u,v,w) 的边权减去 min(w,d)\min(w,d) 后,边权总和最小的同时黑边数量最多。

    • 解析

    首先需要构造出边权和最小的生成树。不难想到最小生成树。在构造出最小生成树后,把最大的边权减去 dd 即可。这样显然是最优的。

    然后考虑黑边数量最多的条件。首先这棵最小生成树的黑边要最多。这个很简单,排序时将边权设为第一关键字,边的颜色设为第二关键字就行了。

    但这棵生成树的黑边个数就是最终答案了吗?

    当然不一定。你可以用一条不在生成树的黑边换掉在生成树的白边,并使得最后的边权和相同。这可以拆解为三个条件:

    • 白边在生成树中黑边两端点组成的路径上。
    • 选出的黑边和白边边权均 d\le d
    • 白边的边权在这棵生成树中最大。

    倍增可以轻松解决问题。时间复杂度 O(mlogm+(n+m)logn)O(m\log m+(n+m)\log n)

    #include<bits/stdc++.h>
    #define ll long long 
    #define For(i,a,b) for(int i=(a);i<=(b);++i)
    #define Rof(i,a,b) for(int i=(a);i>=(b);--i)
    using namespace std;
    const int Maxn=2e5;
    
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(ch<'0' || ch>'9')
        {
            if(ch=='-') f=-1;
            ch=getchar();
        }
        while(ch>='0' && ch<='9')
        {
            x=x*10+ch-'0';
            ch=getchar();
        }
        return x*f;
    }
    
    struct Node{int u,v,w,typ;} Edge[Maxn+5];
    inline bool operator<(Node a,Node b) {return (a.w!=b.w?a.w<b.w:a.typ<b.typ);}
    inline bool operator>(Node a,Node b) {return (a.w!=b.w?a.w>b.w:a.typ>b.typ);}
    int n,m,d,cnt,sum,ans,fa[Maxn+5],vis[Maxn+5];
    inline int Find(int x) {return fa[x]==x?x:fa[x]=Find(fa[x]);}
    
    struct Graph
    {
        struct Point{int to,nxt,w;} E[Maxn*2+5];
        int Head[Maxn+5],tot;
        inline void Addedge(int x,int y,int z)
        {
            E[++tot]=(Point){y,Head[x],z};
            Head[x]=tot;
        }
        int val[Maxn+5],fa[Maxn+5],anc[Maxn+5][20];
        int num[Maxn+5][20],dep[Maxn+5];
        inline int fmin(int x,int y) {return Edge[x]>Edge[y]?x:y;}
        inline void dfs(int x,int f)
        {
            fa[x]=anc[x][0]=f,num[x][0]=val[x],dep[x]=dep[f]+1;
            For(i,1,19) anc[x][i]=anc[anc[x][i-1]][i-1],
                        num[x][i]=fmin(num[x][i-1],num[anc[x][i-1]][i-1]);
            for(int i=Head[x];i;i=E[i].nxt)
            {
                int y=E[i].to;
                if(y==f) continue;
                val[y]=E[i].w,dfs(y,x);
            }
        }
        inline int LCA(int x,int y)
        {
            int res=0;
            if(dep[x]<dep[y]) swap(x,y);
            Rof(i,19,0) if(dep[anc[x][i]]>=dep[y])
                res=fmin(res,num[x][i]),x=anc[x][i];
            if(x==y) return res;
            Rof(i,19,0) if(anc[x][i]!=anc[y][i])
                res=fmin(res,num[x][i]),res=fmin(res,num[y][i]),
                x=anc[x][i],y=anc[y][i];
            return fmin(res,fmin(num[x][0],num[y][0]));
        }
        inline void Build() {dfs(1,0);}
    } G;
    
    int main()
    {
        n=read(),m=read(),d=read();
        For(i,1,m)
        {
            int a=read(),b=read(),c=read();
            if(i<n) Edge[i]=(Node){a,b,c,0};
            else Edge[i]=(Node){a,b,c,1};
        }
        For(i,1,n) fa[i]=i;
        sort(Edge+1,Edge+m+1);
        For(i,1,m)
        {
            int u=Edge[i].u,v=Edge[i].v;
            if(Find(u)!=Find(v))
            {
                fa[Find(u)]=Find(v),vis[i]=1;
                if(!Edge[i].typ) cnt++;
                G.Addedge(u,v,i),G.Addedge(v,u,i);
                sum=max(sum,min(d,Edge[i].w));
            }
        }
        ans=cnt,G.Build();
        For(i,1,m)
        {
            if(vis[i] || Edge[i].typ==1) continue;
            int u=Edge[i].u,v=Edge[i].v,w=Edge[i].w;
            int id=G.LCA(u,v);
            if(Edge[id].typ==0) continue;
            if(sum==Edge[id].w+min(d,Edge[i].w)-Edge[i].w)
                ans=cnt+1;
        }
        printf("%d\n",n-1-ans);
        return 0;
    }
    
    • 1

    信息

    ID
    3835
    时间
    3000ms
    内存
    256MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者