UVA 12587 Reduce the Maintenance Cost 解题报告

来源:互联网 发布:2016网络大电影排行 编辑:程序博客网 时间:2024/06/16 07:23

比赛总结

题目

题意:

一个有n个城市m条道路的图,如果一条边删除后,有些城市变得不可达,则它需要维护,花费为L×D,L是道路长度,D是变得不可达的城市对数。每条道路维护花费由连接的城市中的一个承担。城市本来也有固定的维护花费。求所有可能中,花费最大的城市的最小花费。

题解:

首先可以用tarjan求桥,如果(u,v)之间为桥,且u为父亲,则回溯时已遍历的点数-dfn[v]+1就得到和v在同一双连通分量的城市数。

然后由于桥的性质,将所有非桥边删去后图就变成了森林。对于每一棵树可以单独求最小的最大可能花费:

二分答案,然后遍历树,对于当前点而言,如果任意儿子的子树不能将边全部维护,则这答案不可行;

其次,每条连向儿子的边尽可能让儿子维护,否则父亲维护,如果当前点不能维护所有的这样的边,答案不可行;

看看当前点是否能再维护到父亲的边。


//Time:552ms//Length:2742B#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define MAXN 40010#define MOD 1000000007int he[MAXN],to[MAXN],nex[MAXN],len[MAXN],pri[MAXN],top;int dfn[MAXN],low[MAXN],num,sta[MAXN],stop,cnt[MAXN];bool is[MAXN],vi[MAXN],cou[MAXN][2];void add(int u,int v,int w){    to[top]=v;    len[top]=w;    nex[top]=he[u];    he[u]=top++;}void tarjan(int u,int pre){    dfn[u] = low[u] = num++;    sta[stop++]=u;    for(int i = he[u]; i != -1; i = nex[i])    {        int v = to[i];        if(v==pre) continue;        if(dfn[v]==-1)        {            tarjan(v,u);            if(low[v]>dfn[u])            {                is[i^1] = is[i]=1;                cnt[i^1]=cnt[i]=num-dfn[v];            }            low[u]=min(low[u],low[v]);        }        else low[u] = min(low[u],dfn[v]);    }}bool dfs(int h,long long l,long long lim){    long long sum=pri[h];    cou[h][0]=cou[h][1]=0;    vi[h]=1;    for(int i=he[h];i!=-1;i=nex[i])        if(is[i]&&!vi[to[i]])        {            long long tmp=(long long)len[i]*cnt[i]*(stop-cnt[i]);            if(!dfs(to[i],tmp,lim)) return false;            if(!cou[to[i]][0])   return false;            if(!cou[to[i]][1])   sum+=tmp;        }    if(sum<=lim) cou[h][0]=1;    else    return false;    if(sum+l<=lim)   cou[h][1]=1;    return true;}bool check(long long lim){    for(int i=0;i<stop;++i) vi[sta[i]]=0;    for(int i=0;i<stop;++i)        if(!vi[sta[i]])            if(!dfs(sta[i],0,lim))                return false;    return true;}int main(){    //freopen("/home/moor/Code/input","r",stdin);    int cas,n,m;    long long ans,l,r,mid;    scanf("%d",&cas);    for(int hh=1;hh<=cas;++hh)    {        scanf("%d%d",&n,&m);        l=0;        for(int i=1;i<=n;++i)   scanf("%d",&pri[i]),l=max(l,(long long)pri[i]);        memset(he,-1,sizeof(he));        memset(dfn,-1,sizeof(dfn));        memset(is,0,sizeof(is));        memset(vi,0,sizeof(vi));        memset(cou,0,sizeof(cou));        top=0;        for(int i=0;i<m;++i)        {            int a,b,c;            scanf("%d%d%d",&a,&b,&c);            add(a,b,c);            add(b,a,c);        }        ans=l;        for(int i=1;i<=n;++i)            if(dfn[i]==-1)            {                num=0;                stop=0;                tarjan(i,-1);                l=ans;                r=1e15;                while(l<r)                {                    mid=(l+r)/2;                    if(check(mid))  r=mid;                    else    l=mid+1;                }                ans=max(ans,l);            }        printf("Case %d: ",hh);        cout<<ans<<'\n';    }    return 0;}