您的位置:首页 > 其它

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

2017-11-10 15:24 351 查看
题面

考虑缩点双连通分量,缩点之后成为一棵树,那么加一条边(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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: