【hdu3710】Battle over Cities

来源:互联网 发布:linux怎么运行多线程 编辑:程序博客网 时间:2024/05/18 00:40

Battle over Cities

Description

It is vitally important to have all the cities connected by highways in a war, but some of them are destroyed now because of the war. Furthermore,if a city is conquered, all the highways from/toward that city will be closed by the enemy, and we must repair some destroyed highways to keep other cities connected, with the minimum cost if possible.
Given the map of cities which have all the destroyed and remaining highways marked, you are supposed to tell the cost to connect other cities if each city is conquered by the enemy.

Input

The input contains multiple test cases. The first line is the total number of cases T (T ≤ 10). Each case starts with a line containing 2 numbers N (0 < N ≤ 20000), and M (0 ≤ M ≤ 100000), which are the total number of cities, and the number of highways, respectively. Then M lines follow, each describes a highway by 4 integers: City1 City2 Cost Status where City1 and City2 are the numbers of the cities the highway connects (the cities are numbered from 1 to N), Cost (0 < Cost ≤ 20000) is the effort taken to repair that highway if necessary, and Status is either 0, meaning that highway is destroyed, or 1, meaning that highway is in use.
Note: It is guaranteed that the whole country was connected before the war and there is no duplicated high ways between any two cities.

Output

For each test case, output N lines of integers. The integer in the i-th line indicates the cost to keep the cities connected if the i-th city is conquered by the enemy. In case the cities cannot be connected after the i-th city is conquered by the enemy, output “inf” instead in the corresponding place.

简单中文版题目描述

Description

给定一张N个点,M条边的无向连通图,每条边上有边权w.求删去任意一个点后的最小生成树的边权之和.

Input

多组数据.第一行读入两个数 N,M.接下来M行,每行4个数u,v,d,c,表示有一条点u 点v的无向边,边权为 d(1-c).

Output

对于每组数据,输出N行,表示删去每个点的最小生成树的边权之和.如果不存在,输出inf.

题解:
首先,对于删去每个点都重新最小生成树肯定会超时。于是我们就会想,可不可以先把原图的最小生成树做出来,再在上面做手脚?于是应该怎么做呢?
考虑删去某个点x,就是x与所有儿子和它的父亲的连接切断了。我们要做的是,把它们重新连回去。可供选择的边有两种:连接x子树之间的边和连向x的子树外的边。于是我们先对原图进行一次最小生成树,再分别对每个点把所有可供选择的边预处理出来,再对这些边进行一次最小生成树即可。
怎么预处理呢?
(1)连接子树之间的边。对于每条连接u,v没有在初始最小生成树里面的边,先求出u,v的lca,则这条边就是连接u的dep[u]-dep[lca]-1个father和v的dep[v]-dep[lca]-1个father的子树的边。用倍增求即可。
(2)连向父亲子树外面的边。对于每条连接u,v没有在初始最小生成树里面的边,先求出u,v的lca,则对于u的dep[u]-dep[lca]-2个father到u这条路径上的所有点,这条边都是连到它们父亲的子树外面的。注意,连向父亲子树外面的边只要取最小的一条即可,于是用倍增+树链剖分进行维护连向父亲子树外面的边的最短长度。对于v同理。
但为什么对每个点把所有可供选择的边预处理出来,再对这些边进行一次最小生成树不会超时呢?我们可以这样来想。因为一次Kruskal并查集的find操作是log(n)的,最坏情况下每条边都会用到一次find,因此重点是求出共有多少条边被用到。连接子树之间的边最多m-n条,连向子树外的边最多n条,所以总共进行m次find。对于m-n条不在最小生成树中的边,都进行预处理,一次预处理倍增是log(n)的,树剖是log(n)*log(n)的。因此,总时间复杂度为O(m log n+ m log n log n)。

细节较多,详见代码

代码:

#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;const int N=20005,M=100005,inf=2139062143;int t,n,m,u,v,d,f,ns,rt,cnt,idx,head[N],to[N*2],nxt[N*2];int dep[N],fa[N][20],siz[N],son[N],dfn[N],pos[N],top[N];int minn[N],minv[N*4],tag[N*4];bool use[M];int s,mst,smst,pa[N],w[N],sont[N],num[N];struct edge{    int u,v,d;    bool operator < (const edge &x)const{        return d<x.d;    }}e[M];vector<edge> link[N],edges;void init(){    smst=idx=cnt=rt=0;    memset(use,0,sizeof(use));    memset(head,0,sizeof(head));    memset(dep,0,sizeof(dep));    memset(fa,0,sizeof(fa));    memset(son,0,sizeof(son));    memset(sont,0,sizeof(sont));    memset(w,0,sizeof(w));    memset(minv,127,sizeof(minv));    memset(tag,127,sizeof(tag));    for(int i=1;i<=n;i++){        pa[i]=i;        link[i].clear();    }}void adde(int u,int v){    to[++cnt]=v;    nxt[cnt]=head[u];    head[u]=cnt;}int find(int u){    return u==pa[u]?u:pa[u]=find(pa[u]);}void dfs(int u){    for(int i=1;(1<<i)<=dep[u];i++){        fa[u][i]=fa[fa[u][i-1]][i-1];    }    siz[u]=1;    int v;    for(int i=head[u];i;i=nxt[i]){        v=to[i];        if(v!=fa[u][0]){            fa[v][0]=u;            dep[v]=dep[u]+1;            num[v]=++sont[u];            dfs(v);            siz[u]+=siz[v];            if(!son[u]||siz[son[u]]<siz[v]){                son[u]=v;            }        }    }}void dfs(int u,int tp){    dfn[u]=++idx;    pos[idx]=u;    top[u]=tp;    if(son[u]){        dfs(son[u],tp);    }    int v;    for(int i=head[u];i;i=nxt[i]){        v=to[i];        if(v!=fa[u][0]&&v!=son[u]){            dfs(v,v);        }    }}void pushdown(int o){    minv[o*2]=min(minv[o*2],tag[o]);    minv[o*2+1]=min(minv[o*2+1],tag[o]);    tag[o*2]=min(tag[o*2],tag[o]);    tag[o*2+1]=min(tag[o*2+1],tag[o]);    tag[o]=inf;}void upd(int o,int l,int r,int L,int R,int x){    if(L<=l&&R>=r){        minv[o]=min(minv[o],x);        tag[o]=min(tag[o],x);        return;    }    if(tag[o]!=inf){        pushdown(o);    }    int mid=(l+r)/2;    if(L<=mid){        upd(o*2,l,mid,L,R,x);    }    if(R>mid){        upd(o*2+1,mid+1,r,L,R,x);    }    minv[o]=min(minv[o*2],minv[o*2+1]);}void update(int u,int v,int x){    while(top[u]!=top[v]){        upd(1,1,n,dfn[top[u]],dfn[u],x);        u=fa[top[u]][0];    }    upd(1,1,n,dfn[v],dfn[u],x);}void getmin(int o,int l,int r){    if(l==r){        minn[pos[l]]=minv[o];        return;    }    if(tag[o]!=inf){        pushdown(o);    }    int mid=(l+r)/2;    getmin(o*2,l,mid);    getmin(o*2+1,mid+1,r);}void work(edge &e){    int u=e.u,v=e.v,d;    d=dep[u]-dep[v];    for(int i=0;(1<<i)<=d;i++){        if(d&(1<<i)){            u=fa[u][i];        }    }    if(u==v){        u=e.u;        d=dep[u]-dep[v]-2;        if(d<0){            return;        }        for(int i=0;(1<<i)<=d;i++){            if(d&(1<<i)){                u=fa[u][i];            }        }        update(e.u,u,e.d);        return;    }    int tmpu=u,tmpv=v;    for(int i=15;i>=0;i--){        if(fa[tmpu][i]!=fa[tmpv][i]){            tmpu=fa[tmpu][i];            tmpv=fa[tmpv][i];        }    }    link[fa[tmpu][0]].push_back((edge){tmpu,tmpv,e.d});    d=dep[e.u]-dep[tmpu]-1;    if(d>=0){        u=e.u;        for(int i=0;(1<<i)<=d;i++){            if(d&(1<<i)){                u=fa[u][i];            }        }        update(e.u,u,e.d);    }    d=dep[e.v]-dep[tmpv]-1;    if(d>=0){        v=e.v;        for(int i=0;(1<<i)<=d;i++){            if(d&(1<<i)){                v=fa[v][i];            }        }        update(e.v,v,e.d);    }}int main(){    scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&m);        init();        for(int i=1;i<=m;i++){            scanf("%d%d%d%d",&e[i].u,&e[i].v,&d,&f);            e[i].d=d*(1-f);        }        sort(e+1,e+m+1);        ns=0;        for(int i=1;i<=m&&ns<n-1;i++){            u=find(e[i].u),v=find(e[i].v);            if(u!=v){                use[i]=true;                pa[v]=u;                ns++;                adde(e[i].u,e[i].v);                adde(e[i].v,e[i].u);                w[e[i].u]+=e[i].d;                w[e[i].v]+=e[i].d;                smst+=e[i].d;                if(!rt){                    rt=e[i].u;                }            }        }        dfs(rt);        dfs(rt,rt);        for(int i=1;i<=m;i++){            if(!use[i]){                if(dep[e[i].u]<dep[e[i].v]){                    swap(e[i].u,e[i].v);                }                work(e[i]);            }        }        getmin(1,1,n);        for(int i=1;i<=n;i++){            edges.clear();            s=sont[i];            if(fa[i][0]){                s++;                for(int j=head[i];j;j=nxt[j]){                    v=to[j];                    if(v!=fa[i][0]&&minn[v]!=inf){                        edges.push_back((edge){num[v],s,minn[v]});                    }                }            }            for(int j=0;j<link[i].size();j++){                edges.push_back((edge){num[link[i][j].u],num[link[i][j].v],link[i][j].d});            }            sort(edges.begin(),edges.end());            mst=0;            for(int j=1;j<=s;j++){                pa[j]=j;            }            ns=0;            for(int j=0;j<edges.size()&&ns<s-1;j++){                u=find(edges[j].u),v=find(edges[j].v);                if(u!=v){                    pa[v]=u;                    ns++;                    mst+=edges[j].d;                }            }            if(ns<s-1){                puts("inf");            }else{                printf("%d\n",smst-w[i]+mst);            }        }    }    return 0;}
原创粉丝点击