文章标题 UVALive 6062:Reduce the Maintenance Cost(双联通分量缩点)

来源:互联网 发布:2017淘宝注册用户数量 编辑:程序博客网 时间:2024/06/11 23:07

Reduce the Maintenance Cost

题意:在有n(n <= 10000)个点的无向图上,定义有m条边,每条边有自己的长度L,还有一个维护值val=N*L,其中N的定义是
N=破坏掉这条边时有多少点对不连通。
每条边的val值需要连接这条边两个点中的一个点来承担,现在每个点有一个初始值,问怎样分配使得所有点中最大的值最小。
分析:可以知道的是,只有桥才有val值,其他不是桥的边的val值都是0,因为没有点对会由于这条边被删除而不连通,所以我们可以通过tarjan缩点然后重新建树(森林),这样新建的树(森林)中的边就是桥的,而桥可以通过树形DP来求出每个子树的节点数。
代码:

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <queue>#include <set>#include <map>#include <algorithm>#include <math.h>#include <vector>using namespace std;typedef long long ll;const int mod=1e9+7;const int maxn=1e5+10;ll a[maxn];int n,m;ll mid;struct Edge{    int from,to,nex;    ll w;    bool cnt;//是否为桥 }edge[maxn];int head[maxn],tot;int Low[maxn],dfn[maxn],st[maxn],belong[maxn];int idx,top;int block; //联通块的数目 bool Instack[maxn];int bridge;//桥的数目 int num[maxn];void init(){    tot=0;memset (head,-1,sizeof (head));    memset (dfn,0,sizeof (dfn));    memset (Instack,false,sizeof (Instack));    memset (num,0,sizeof (num));    idx=top=block=bridge=0;}void addedge(int u,int v,int w){    edge[tot]=Edge{u,v,head[u],w,0};    head[u]=tot++;}void Tarjan(int u,int pre){    Low[u]=dfn[u]=++idx;    st[top++]=u;    Instack[u]=true;    for (int i=head[u];i!=-1;i=edge[i].nex){        int v=edge[i].to;        if (v==pre)continue;        if (!dfn[v]){            Tarjan(v,u);            if (Low[u]>Low[v])Low[u]=Low[v];            if (Low[v]>dfn[u]){                bridge++;edge[i].cnt=true;edge[i^1].cnt=true;            }        }else if (Instack[v]&&Low[u]>dfn[v]){            Low[u]=dfn[v];        }    }    if (Low[u]==dfn[u]){        block++;        int v;        do{            v=st[--top];            Instack[v]=false;            belong[v]=block;            num[block]++;        }while (v!=u);    }}struct node {//重新建图用的节点     int u,v,from,to,nex;//from表示的是这条边在原来的图上连接的两个节点,下面用来+val值用的     ll d;}e[maxn];int first[maxn];void add(int u,int v,int from,int to,ll d){    e[tot]=node{u,v,from,to,first[u],d};    first[u]=tot++; }int sz[maxn],fa_id[maxn];//fa_id[u]用来标记第几条边 void dfs(int u,int fa){//计算每个节点的孩子数     sz[u]=num[u];fa_id[u]=-1;    for (int i=first[u];i!=-1;i=e[i].nex){        int v=e[i].v;        if (v==fa){            fa_id[u]=i;            continue;        }        dfs(v,u);        sz[u]+=sz[v];    }} //计算每条边的权值int vis[maxn]; void dfs2(int u,int fa,int treenode){//treenode树的节点数     vis[u]=1;    for (int i=first[u];i!=-1;i=e[i].nex){        int v=e[i].v;        if (v==fa){            e[i].d*=(ll)sz[u]*(ll)(treenode-sz[u]);            e[i^1].d=e[i].d;            continue;        }        dfs2(v,u,treenode);    }}void work(){//计算每个节点的孩子数目,和每条桥的价值    memset (sz,0,sizeof (sz));    for (int i=1;i<=block;i++){        if (sz[i]==0)dfs(i,-1);    }     memset (vis,0,sizeof (vis));    for (int i=1;i<=block;i++){        if (vis[i]==0)dfs2(i,-1,sz[i]);    } }ll dp[maxn];bool DFS(int u,int fa){    vis[u]=1;    for (int i=first[u];i!=-1;i=e[i].nex){        int v=e[i].v;        if (v==fa)continue;        if (!DFS(v,u))return false;    }    if (fa_id[u]!=-1){        int oldfrom=e[fa_id[u]].from,oldto=e[fa_id[u]].to;        ll val=e[fa_id[u]].d;        if (dp[oldfrom]+val<=mid)dp[oldfrom]+=val;//尽可能加在叶子节点         else if (dp[oldto]+val<=mid)dp[oldto]+=val;        else return false;    }    return dp[u]<=mid;}bool ok(){    for (int i=1;i<=n;i++)dp[i]=a[i];     memset (vis,0,sizeof (vis));    for (int i=1;i<=block;i++){        if (vis[i]==0){            if (!DFS(i,-1))return false;        }    }    return true;}int main(){    int T;    scanf ("%d",&T);    int cas=1;    while (T--){        init();        scanf ("%d%d",&n,&m);         ll hi=(ll)1e18;        ll lo=0;        for (int i=1;i<=n;i++){            scanf ("%lld",&a[i]);            lo=max((ll)a[i],lo);        }        int u,v,w;        for (int i=0;i<m;i++){            scanf ("%d%d%d",&u,&v,&w);            addedge(u,v,w);addedge(v,u,w);        }        for (int i=1;i<=n;i++)if (!dfn[i])Tarjan(i,-1);        int tot2=tot;        memset (first,-1,sizeof (first));tot=0;        for (int i=0;i<tot2;i+=2){            int u=edge[i].from,v=edge[i].to,w=edge[i].w;            if (belong[u]==belong[v])continue;            add(belong[u],belong[v],u,v,w);//重新建图             add(belong[v],belong[u],v,u,w);        }        work();//计算每个节点的孩子数目,和每条桥的价值        ll ans=hi+1;        while (lo<=hi){            mid=(lo+hi)/2;            if (ok()){                hi=mid-1;                ans=min(ans,mid);             }else lo=mid+1;         }        printf ("Case %d: %lld\n",cas++,ans);    }    return 0;}/*32 1 5 10 1 2 106 6 10 20 30 40 50 60 1 2 1 2 3 1 1 3 1 1 4 6 1 5 6 4 6 23 1 10 20 30 2 3 10*/
阅读全文
0 0