BZOJ 3331 (Tarjan縮點+樹上差分)
阿新 • • 發佈:2018-11-06
題面
分析
用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]); } }