1. 程式人生 > >BZOJ 3331 (Tarjan縮點+樹上差分)

BZOJ 3331 (Tarjan縮點+樹上差分)

題面

傳送門

分析

用Tarjan求出割點,對點-雙連通分量(v-DCC)進行縮點,圖會變成一棵樹
注意v-DCC的縮點和e-DCC不同,因為一個割點可能屬於多個v-DCC
設圖中共有p個割點和t個v-DCC,我們建立一張包含p+t個點的新圖,並將每個割點和包含它的所有v-DCC連邊
縮點後原圖中一般點的編號為v-DCC的編號,第i個割點的編號為(v-DCC個數+i)
對於原圖上的一條路徑(u,v),找到u,v對應的新編號,用樹上差分演算法更新路徑上的所有點,使次數+1
為了處理若u,v不是割點,無法更新u,v的訪問次數(因為在新圖上v-DCC上的所有點被縮成了一個大點,而我們卻要對點u,v單獨進行更新
因此,我們在原圖上建立一個數組graph_count,記錄第i號節點(不是割點)的訪問次數
輸出答案時:
-若i是割點,直接輸出樹上對應的割點的訪問次數
-否則輸出graph_count[i]

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#define maxn 200005
#define maxm 200005
#define maxlog 32
using namespace std;
int n,m,q;
inline int qread(){
    int x=0,sign=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') sign=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
    return x*sign;
}

struct graph {
    struct edge {
        int from;
        int to;
        int next;
    } E[maxm<<1];
    int head[maxn];
    int ecnt;
    void add_edge(int u,int v) {
        ecnt++;
        E[ecnt].from=u;
        E[ecnt].to=v;
        E[ecnt].next=head[u];
        head[u]=ecnt;
    }
    graph(){
        memset(head,0,sizeof(head));
        memset(E,0,sizeof(E));
        ecnt=1;
    }
};
graph G,T;

int tim,cnt,newn;
int dfn[maxn];
int low[maxn];
int cut[maxn];
int new_id[maxn]; 
int belong[maxn];
stack<int>s;
vector<int>v_dcc[maxn];
void tarjan(int x){
    int flag=0;
    dfn[x]=low[x]=++tim;
    s.push(x);
    for(int i=G.head[x];i;i=G.E[i].next){
        int y=G.E[i].to;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(dfn[x]<=low[y]){
                flag++;
                if(x!=1||flag>1) cut[x]=1;
                cnt++;
                int z;
                do{
                    z=s.top();
                    s.pop();
                    v_dcc[cnt].push_back(z);
                }while(z!=y);
                v_dcc[cnt].push_back(x);
            } 
        }else low[x]=min(low[x],dfn[y]);
    }
}

void graph_to_tree(){
    tim=cnt=0;
    tarjan(1);
    newn=cnt;
    for(int i=1;i<=n;i++){
        if(cut[i]){
            belong[i]=++newn;
        }
    }
    for(int i=1;i<=cnt;i++){
        for(int j=0;j<v_dcc[i].size();j++){
            int x=v_dcc[i][j];
            if(cut[x]){
                T.add_edge(i,belong[x]);
                T.add_edge(belong[x],i);
            }
            else belong[x]=i;
        }
    }
}

int graph_count[maxn];
int tree_count[maxn];
int deep[maxn];
int anc[maxn][maxlog];
void lca_init(int x,int fa){
    deep[x]=deep[fa]+1;
    anc[x][0]=fa;
    for(int i=1;i<=20;i++){
        anc[x][i]=anc[anc[x][i-1]][i-1];
    }
    for(int i=T.head[x];i;i=T.E[i].next){
        int y=T.E[i].to;
        if(y!=fa){
            lca_init(y,x);
        }
    }
}

int lca(int x,int y){
    if(deep[x]<deep[y]) swap(x,y);
    for(int i=20;i>=0;i--){
        if(deep[anc[x][i]]>=deep[y]){
            x=anc[x][i];
        }
    }
    if(x==y) return x;
    for(int i=20;i>=0;i--){
        if(anc[x][i]!=anc[y][i]){
            x=anc[x][i];
            y=anc[y][i];
        }
    }
    return anc[x][0];
}

void add_route(int u,int v){
    int l=lca(u,v);
    tree_count[l]--;
    tree_count[anc[l][0]]--;
    tree_count[u]++;
    tree_count[v]++; 
}

void sum_up(int x,int fa){
    for(int i=T.head[x];i;i=T.E[i].next){
        int y=T.E[i].to;
        if(y!=fa){
            sum_up(y,x);
            tree_count[x]+=tree_count[y];
        }
    }
}
int main() {
    int u,v,nu,nv;
    n=qread();
    m=qread();
    q=qread();
    for(int i=1;i<=m;i++){
        u=qread();
        v=qread();
        G.add_edge(u,v);
        G.add_edge(v,u);
    }
    graph_to_tree();
    lca_init(1,0);
    for(int i=1;i<=q;i++){
        u=qread();
        v=qread();
        nu=belong[u];
        nv=belong[v];
        add_route(nu,nv);
        if(!cut[u]) graph_count[u]++;
        if(!cut[v]) graph_count[v]++;
    }
    sum_up(1,0);
    for(int i=1;i<=n;i++){
        if(cut[i]){
            graph_count[i]=tree_count[belong[i]];
        }
    }
    for(int i=1;i<=n;i++){
        printf("%d\n",graph_count[i]);
    }
}