[bzoj3331][BeiJing2013]压力(tarjan点双缩点+树上差分)
题目链接:传送门
必须经过的点,也就是绕不开的点,就是割点(这就是割点的定义)。那么对于所有询问,非割点的必须经过次数,就是这个点作为询问的开头或者结尾的次数。
找出所有的点双联通分量,然后缩点(这里注意把割点单独作为一个点),它将是一棵树。然后把询问放到树上,作树上差分。
要注意的是新图的节点数可能大于旧图,我因为忽略了这个在lca的init函数里没注意上界,wa了很多次。
代码比较复杂,但都是模块化的,比较好调。
代码:
#include<iostream> #include<cstring> #include<cstdio> #include<stack> #include<vector> #define maxn 200005 using namespace std; inline void read(int &x){ x=0;int f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } int N,M,Q; struct node{ int nex,from,to; }edge[maxn<<2][2]; int head[maxn][2],tot[2]; inline void insert(int from,int to,int ty){ edge[++tot[ty]][ty].nex=head[from][ty]; head[from][ty]=tot[ty]; edge[tot[ty]][ty].from=from; edge[tot[ty]][ty].to=to; } int dfn[maxn],low[maxn],id,belong[maxn],cnt; stack<int>sta; bool cut[maxn]; vector<int> q[maxn]; void tarjan(int x){ int flag=0; dfn[x]=low[x]=++id; sta.push(x); for(int i=head[x][0];i;i=edge[i][0].nex){ int y=edge[i][0].to; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); if(dfn[x]<=low[y]){ int u;++cnt;++flag; if(flag>1||x!=1) cut[x]=1; do{ u=sta.top();//cout<<"out: "<<u<<" "<<x<<endl; sta.pop(); q[cnt].push_back(u); }while(u!=y); q[cnt].push_back(x); } }else low[x]=min(low[x],dfn[y]); } } int anc[maxn][20],dept[maxn]; void dfs1(int x,int u){ anc[x][0]=u; dept[x]=dept[u]+1; for(int i=head[x][1];i;i=edge[i][1].nex) if(edge[i][1].to^u) dfs1(edge[i][1].to,x); } int num; void init(){ for(int i=1;i<=18;i++) for(int a=1;a<=num;a++) anc[a][i]=anc[anc[a][i-1]][i-1]; } int lca(int u,int v){ if(dept[u]<dept[v]) swap(u,v); for(int i=18;i>=0;i--) if(dept[anc[u][i]]>=dept[v]){ //cout<<u<<" "<<i<<" "; u=anc[u][i]; } //cout<<u<<" "<<v<<endl; if(u==v) return u; for(int i=18;i>=0;i--) if(anc[u][i]!=anc[v][i]){ u=anc[u][i]; v=anc[v][i]; } return anc[u][0]; } int sum[maxn]; void suodian(){ int u,v; num=cnt; for(int i=1;i<=N;i++) if(cut[i]) belong[i]=++num; for(int x=1;x<=cnt;x++){ for(int j=0;j<q[x].size();j++){ int y=q[x][j]; if(cut[y]){ insert(x,belong[y],1); insert(belong[y],x,1); }else belong[y]=x; } } } int dfs(int x,int u){ //cout<<x<<" "; for(int i=head[x][1];i;i=edge[i][1].nex) if(edge[i][1].to^u){ dfs(edge[i][1].to,x); sum[x]+=sum[edge[i][1].to]; } } int final[maxn]; int main(){ //freopen("1.in","r",stdin); //freopen("1.ans","w",stdout); read(N);read(M);read(Q); int u,v; for(int i=1;i<=M;i++){ read(u);read(v); insert(u,v,0); insert(v,u,0); } tarjan(1); suodian(); dfs1(1,0); init(); //for(int i=1;i<=20;i++) //cout<<anc[90623][i]<<" "; while(Q--){ read(u);read(v); final[u]++;final[v]++; u=belong[u];v=belong[v]; sum[u]++;sum[v]++; int ancs=lca(u,v); //cout<<"lca: "<<u<<" "<<v<<" "<<ancs<<endl; sum[ancs]--; if(ancs!=1) sum[anc[ancs][0]]--; } dfs(1,1); for(int i=1;i<=N;i++) if(!cut[i]) printf("%d\n",final[i]); else printf("%d\n",sum[belong[i]]); }