HDU3710 Battle over Cities(最小生成樹+樹鏈剖分+倍增+線段樹)

Battle over Cities

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 467    Accepted Submission(s): 125


Problem 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.
 

 

Sample Input
3 4 5 1 2 1 1 1 3 1 1 2 3 1 0 2 4 1 1 3 4 2 0 4 5 1 2 1 1 1 3 1 1 2 3 1 0 2 4 1 1 3 4 1 0 3 2 1 2 1 1 1 3 1 1
 

 

Sample Output
1 2 0 0 1 1 0 0 inf 0 0
 

 

Author
GUAN, Yao
 

 

Source
 

 

Recommend
zhengfeng   |   We have carefully selected several similar problems for you:  3719 3718 3717 3716 3715 

 題解:給定一張N個點,M條邊的無向連通圖,每條邊上有邊權w,求刪去每一個後的最小生成樹。
思路:如果暴力刪除每個點後重新建圖,跑 Kruskal的話 時間複雜度O(NM),顯然不行。
我們考慮先求出沒有刪點的最小生成樹,然後如果我們刪除一個點,那麼和這個點相連的邊全都得去掉,只剩下 該點的鄰接點(子節點)和它的父親節點(可能沒有),那麼我們只需要在我的子節點連向我的父親節點的邊和我的子樹之間的連邊中跑一遍MST就行了。

怎麼預處理呢?
(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<bits/stdc++.h>
using namespace std;
const int N=2e4+10;
const int M=1e5+10;
const int inf=2139062143;
int T,n,m,ns,cnt,tim,head[N],to[N<<1],nxt[N<<1];
int dep[N],fa[N][20],siz[N],son[N],dfn[N],pos[N],top[N];
int minn[N],minv[N<<2],tag[N<<2];
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=tim=cnt=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 AddEdge(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 dfs1(int u)
{
    for(int i=1;(1<<i)<=dep[u];++i)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    siz[u]=1;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa[u][0]) continue;
        fa[v][0]=u;
        dep[v]=dep[u]+1;
        num[v]=++sont[u];
        dfs1(v);
        siz[u]+=siz[v];
        if(!son[u]||siz[son[u]]<siz[v]) son[u]=v;
    }
}
void dfs2(int u,int tp)
{
    dfn[u]=++tim;
    pos[tim]=u;
    top[u]=tp;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v!=fa[u][0]&&v!=son[u]) dfs2(v,v);
    }
}
void pushdown(int rt)
{
    minv[rt<<1]=min(minv[rt<<1],tag[rt]);
    minv[rt<<1|1]=min(minv[rt<<1|1],tag[rt]);
    tag[rt<<1]=min(tag[rt<<1],tag[rt]);
    tag[rt<<1|1]=min(tag[rt<<1|1],tag[rt]);
    tag[rt]=inf;
}
void upd(int rt,int l,int r,int L,int R,int x)
{
    if(L<=l&&R>=r)
    {
        minv[rt]=min(minv[rt],x);
        tag[rt]=min(tag[rt],x);
        return;
    }
    if(tag[rt]!=inf) pushdown(rt);
    int mid=(l+r)/2;
    if(L<=mid) upd(rt<<1,l,mid,L,R,x);
    if(R>mid) upd(rt<<1|1,mid+1,r,L,R,x);
    minv[rt]=min(minv[rt<<1],minv[rt<<1|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 rt,int l,int r)
{
    if(l==r)
    {
        minn[pos[l]]=minv[rt];
        return;
    }
    if(tag[rt]!=inf) pushdown(rt);
    int mid=(l+r)/2;
    getmin(rt<<1,l,mid);
    getmin(rt<<1|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();
        int d,f;
        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++)
        {
            int u=Find(e[i].u),v=Find(e[i].v);
            if(u!=v)
            {
                use[i]=true;
                pa[v]=u;
                ns++;
                AddEdge(e[i].u,e[i].v);
                AddEdge(e[i].v,e[i].u);
                w[e[i].u]+=e[i].d;
                w[e[i].v]+=e[i].d;
                smst+=e[i].d;
            }
        }
        dfs1(1); dfs2(1,1);
        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])//把所有連向i上面的邊加進去
            {
                ++s;
                for(int j=head[i];j;j=nxt[j])
                {
                    int 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++)//把i的鄰接點之間的連邊加進去
                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++)
            {
                int 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;
}
View Code

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章