蓝书(算法竞赛进阶指南)刷题记录——POJ3694 Network

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82944048

题目:PO3694.

题目大意:给出一张图,然后给出Q组连接操作,输出每一次操作后的割边(桥)数量.

注:这道题给出的图是连通图.

首先这道题可以先用tarjan缩点,我们可以得出一棵树,这棵树上的每一条边都是割边.

之后每一次加边,若加入的边在一个点内,则直接跳过.

否则我们设这条边的两个顶点所在的树上节点为u和v,我们发现u到v路径上的所有边都不再是割边,所以我们将所有在u到v的路径上的点暴力缩到一个点内,这样做的时间复杂度为O(m+q*n)的.由于这道题时限宽松,这个做法可以过.

暴力缩点的时候我们先暴力求出LCA,然后我们可以分别处理两条链,并将所有链上的点的标记打上LCA的标记(但是我直接写并査集了不展示了,我才不会告诉你其实是我写的暴力挂了...).

但是这个做法还可以优化,我们发现图一定是连通,所以缩完的树连边一定不会将两棵树并为一棵,所以我们可以用并查集来维护这个问题,时间复杂度O(m+qlogn).

所以我们可以先tarjan缩点建树,然后每一次询问倍增求出LCA,并将链用并査集合并.注意这里的倍增求出LCA与并査集合并并不冲突,我们维护的其实是两棵树,一棵是静态的树,用于倍增求LCA,一棵是动态的树,用并査集来维护.

代码如下:

//#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
  using namespace std;
#define Abigail inline void
const int N=100000,M=200000;
int n,m;
struct graph_side{
  int y,next;
  bool br;
}e[M*2+9];
int lin[N+9],t;
int dfn[N+9],low[N+9],edcc[N+9],dcc,num,ans;
struct tree_side{
  int y,next;
}ec[N*2+9];
int linc[N+9],tc;
int deep[N+9],gr[N+9][19];
int fa[N+9];
void ins(int x,int y){
  e[++t].y=y;
  e[t].next=lin[x];
  lin[x]=t;
}
void insc(int x,int y){
  ec[++tc].y=y;
  ec[tc].next=linc[x];
  linc[x]=tc;
}
void tarjan(int k,int fa){
  dfn[k]=low[k]=++num;
  for (int i=lin[k];i;i=e[i].next){
    int y=e[i].y;
    if (!dfn[y]){
      tarjan(y,i);
      low[k]=min(low[k],low[y]);
      if (low[y]>dfn[k]) e[i].br=e[i^1].br=1;
    }else if (i^fa^1) low[k]=min(low[k],dfn[y]);
  }
}
void dfs(int k){
  edcc[k]=dcc;
  for (int i=lin[k];i;i=e[i].next)
    if (!edcc[e[i].y]&&!e[i].br) dfs(e[i].y);
}
void contract(){
  for (int i=1;i<=n;i++)
    if (!dfn[i]) tarjan(i,0);
  for (int i=1;i<=n;i++)
    if (!edcc[i]) ++dcc,dfs(i);
  for (int i=2;i<=t;i++){
    int x=e[i^1].y,y=e[i].y;
    if (edcc[x]==edcc[y]) continue;
    insc(edcc[x],edcc[y]);
  }
}
void dfs_lca(int k,int fa){
  deep[k]=deep[fa]+1;
  gr[k][0]=fa;
  for (int i=1;i<19;i++)
    gr[k][i]=gr[gr[k][i-1]][i-1];
  for (int i=linc[k];i;i=ec[i].next)
    if (ec[i].y^fa) dfs_lca(ec[i].y,k);
}
int LCA(int x,int y){
  if (deep[x]<deep[y]) swap(x,y);
  for (int i=18;i>=0;i--)
    if (deep[gr[x][i]]>=deep[y]) x=gr[x][i];
  if (x==y) return x;
  for (int i=18;i>=0;i--)
    if (gr[x][i]^gr[y][i]) x=gr[x][i],y=gr[y][i];
  return gr[x][0];
}
int get(int u){
  return u^fa[u]?fa[u]=get(fa[u]):u;
}
void access(int from,int to){
  from=get(from);
  while (deep[from]>deep[to]){
    fa[from]=gr[from][0];
    ans--;
    from=get(from);
  }
}
Abigail start(){
  t=tc=1;num=dcc=0;
  for (int i=1;i<=n;i++)
    linc[i]=lin[i]=dfn[i]=low[i]=edcc[i]=deep[i]=0;
  for (int i=1;i<=2*m+1;i++)
    e[i].br=e[i].y=e[i].next=ec[i].y=ec[i].next=0;
}
Abigail into(){
  int x,y;
  for (int i=1;i<=m;i++){
    scanf("%d%d",&x,&y);
    ins(x,y);ins(y,x);
  }
}
Abigail work(){
  contract();
  dfs_lca(1,0);
  for (int i=1;i<=dcc;i++) fa[i]=i;
}
Abigail getans(int cas){
  int q,x,y;ans=dcc-1;
  printf("Case %d:\n",cas);
  scanf("%d",&q);
  for (int i=1;i<=q;i++){
    scanf("%d%d",&x,&y);
    x=edcc[x],y=edcc[y];
    int lca=LCA(x,y);
    access(x,lca);access(y,lca);
    printf("%d\n",ans);
  }
  printf("\n");
}
int main(){
  for (int cas=1;~scanf("%d%d",&n,&m)&&n+m;cas++){
    start();
    into();
    work();
    getans(cas);
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/82944048