zoukankan      html  css  js  c++  java
  • 题解 洛谷 P3639 【[APIO2013]道路费用 】

    不难想到可以(2^k)去枚举(k)条新边的选择方案,然后加入原图中的边来使图连通,用当前方案的收益去更新答案,但是这样复杂度过不去。

    可以先把(k)条新边都连上,然后再加入边权从小到大排序过后的原图的边,直到图连通。后加入的原图的边在任何一个新边的选择方案都是要加入的,因为找这些边时是选了所有(k)条新边,其他方案只会比这时选择更少的新边,所以为保证连通,这些后加入的边肯定是要选择的,可能还要加入更多的原图中的边,同时这些边是按边权排序后的,所以也能满足题目中最小生成树的要求。

    根据原图中边权互不相同,所以这些后加入的边的集合是唯一的。因为这些后加入的边是必选的,所以可以把只考虑这些边的连通块缩成点,发现缩点后的数量最多为(k+1)

    上面也说到,可能在一个新边的选择方案中,还需加入更多的原图中的边,这些边就是使这(k+1)个点连通的边,把这(k)条边记录下来,作为处理选择方案时的备选边。

    然后就可以按之前的做法来处理了,(2^k)去枚举(k)条新边的选择方案,然后加入这(k)条备选边来使图连通,然后用当前方案的收益去更新答案。

    具体实现加边判断连通性和缩点时用并查集比较方便。

    总复杂度为(O(m log m + 2^k k^2))

    (code:)

    #include<bits/stdc++.h>
    #define maxn 3000010
    #define inf 1000000000
    using namespace std;
    typedef long long ll;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int n,m,k,root,cnt,tot;
    ll ans,sum;
    int id[maxn],fa[maxn],de[maxn];
    ll p[maxn],pe[maxn],siz[maxn],mi[maxn];
    bool flag;
    struct edge
    {
        int to,nxt;
    }e[maxn];
    int head[maxn],edge_cnt;
    void add(int from,int to)
    {
        e[++edge_cnt]=(edge){to,head[from]};
        head[from]=edge_cnt;
    }
    struct Edge
    {
        int x,y,v;
    }e1[maxn],e2[maxn],e3[maxn];
    bool cmp(const Edge &a,const Edge &b)
    {
        return a.v<b.v;
    }
    struct node
    {
        int f[maxn];
        int find(int x)
        {
            return f[x]==x?x:f[x]=find(f[x]);
        }
        void merge(int x,int y)
        {
            x=find(x),y=find(y);
            if(x==y) return;
            f[x]=y;
        }
    }A,B;
    void dfs(int x,int fath)
    {
        fa[x]=fath,de[x]=de[fath]+1,siz[x]=pe[x];
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;
            if(y==fath) continue;
            dfs(y,x),siz[x]+=siz[y];
        }
    }
    int main()
    {
        read(n),read(m),read(k);
        for(int i=1;i<=n;++i) A.f[i]=B.f[i]=i;
        for(int i=1;i<=m;++i)
            read(e1[i].x),read(e1[i].y),read(e1[i].v);
        sort(e1+1,e1+m+1,cmp);
        for(int i=1;i<=k;++i)
            read(e2[i].x),read(e2[i].y);
        for(int i=1;i<=n;++i) read(p[i]);
        for(int i=1;i<=k;++i) A.merge(e2[i].x,e2[i].y);
        for(int i=1;i<=m;++i)
        {
            int x=e1[i].x,y=e1[i].y;
            if(A.find(x)==A.find(y)) continue;
            A.merge(x,y),B.merge(x,y);
        }
        for(int i=1;i<=n;++i)
            if(B.find(i)==i)
                id[i]=++tot;
        root=id[B.find(1)],A=B;
        for(int i=1;i<=n;++i) pe[id[B.find(i)]]+=p[i];
        for(int i=1;i<=m;++i)
        {
            int x=e1[i].x,y=e1[i].y;
            if(B.find(x)==B.find(y)) continue;
            B.merge(x,y),e3[++cnt]=e1[i];
        }
        for(int i=1;i<=k;++i) e2[i].x=id[A.find(e2[i].x)],e2[i].y=id[A.find(e2[i].y)];
        for(int i=1;i<=cnt;++i) e3[i].x=id[A.find(e3[i].x)],e3[i].y=id[A.find(e3[i].y)];
        for(int S=0;S<(1<<k);++S)
        {
            edge_cnt=sum=flag=0;
            for(int i=1;i<=tot;++i) A.f[i]=i,head[i]=0,mi[i]=inf;
            for(int i=1;i<=k;++i)
            {
                if(!(S&(1<<(i-1)))) continue;
                int x=e2[i].x,y=e2[i].y;
                if(A.find(x)==A.find(y))
                {
                    flag=true;
                    break;
                }
                A.merge(x,y),add(x,y),add(y,x);
            }
            if(flag) continue;
            for(int i=1;i<=cnt;++i)
            {
                int x=e3[i].x,y=e3[i].y;
                if(A.find(x)==A.find(y)) continue;
                A.merge(x,y),add(x,y),add(y,x);
            }
            dfs(root,0);
            for(int i=1;i<=cnt;++i)
            {
                int x=e3[i].x,y=e3[i].y;
                ll v=e3[i].v;
                while(x!=y)
                {
                    if(de[x]<de[y]) swap(x,y);
                    mi[x]=min(mi[x],v),x=fa[x];
                }
            }
            for(int i=1;i<=k;++i)
            {
                if(!(S&(1<<(i-1)))) continue;
                int x=e2[i].x,y=e2[i].y;
                if(de[x]<de[y]) swap(x,y);
                sum+=mi[x]*siz[x];
            }
            ans=max(ans,sum);
        }
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    css之position
    js之循环语句
    js之条件判断
    js之字典操作
    js之获取html标签的值
    5.15 牛客挑战赛40 C 小V和字符串 数位dp 计数问题
    5.21 省选模拟赛 luogu P4297 [NOI2006]网络收费 树形dp
    luogu P4525 自适应辛普森法1
    luogu P1784 数独 dfs 舞蹈链 DXL
    5.21 省选模拟赛 luogu P4207 [NOI2005]月下柠檬树 解析几何 自适应辛普森积分法
  • 原文地址:https://www.cnblogs.com/lhm-/p/13064434.html
Copyright © 2011-2022 走看看