[2017纪中11-9]道路重建 点双连通分量+树的直径

来源:互联网 发布:网络硬盘录相机价格 编辑:程序博客网 时间:2024/06/05 20:13

题面
考虑缩点双连通分量,缩点之后成为一棵树,那么加一条边(x,y)可以使得原树上x到y的路径上的所有边变得不危险,于是跑个树直径即可。
代码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int maxn=200010;int n,m,tim,top,nc,dfn[maxn],low[maxn],col[maxn],st[maxn],mx,id;bool vis[maxn];struct edge{    int t;    edge *next,*rev;}*con[maxn],*sd[maxn];void ins(int x,int y){    edge *p=new edge;    p->t=y;    p->next=con[x];    con[x]=p;    p=new edge;    p->t=x;    p->next=con[y];    con[y]=p;    con[x]->rev=con[y];    con[y]->rev=con[x];}void ins2(int x,int y){    edge *p=new edge;    p->t=y;    p->next=sd[x];    sd[x]=p;}void tarjan(int v,edge *fro){    vis[v]=1;    dfn[v]=low[v]=++tim;    st[++top]=v;    for(edge *p=con[v];p;p=p->next)        if(fro==NULL||p!=fro->rev)        {            if(vis[p->t]) low[v]=min(low[v],dfn[p->t]);            else tarjan(p->t,p),low[v]=min(low[v],low[p->t]);        }    if(fro==NULL||low[v]>dfn[fro->rev->t])    {        nc++;        while(st[top+1]!=v) col[st[top--]]=nc;    }}void suodian(){    for(int i=1;i<=n;i++)        for(edge *p=con[i];p;p=p->next)            if(col[i]!=col[p->t]) ins2(col[i],col[p->t]);}void dfs(int v,int d){    vis[v]=1;    if(d>mx) mx=d,id=v;    for(edge *p=sd[v];p;p=p->next)        if(!vis[p->t]) dfs(p->t,d+1);}int main(){    while(1)    {        for(int i=1;i<=n;i++)               con[i]=NULL,sd[i]=NULL;        scanf("%d%d",&n,&m);        if(n==0&&m==0) break;        for(int i=1;i<=m;i++)        {            int x,y;            scanf("%d%d",&x,&y);            ins(x,y);        }        memset(dfn,0,sizeof(dfn));        memset(low,0,sizeof(low));        memset(col,0,sizeof(col));        memset(vis,0,sizeof(vis));        memset(st,0,sizeof(st));        tim=top=nc=mx=id=0;        tarjan(1,NULL);        suodian();        memset(vis,0,sizeof(vis));        dfs(1,1);        memset(vis,0,sizeof(vis));        mx=0;        dfs(id,1);        printf("%d\n",nc-mx);    }    return 0;}
原创粉丝点击