HDU 2460 Network
题意
给一个连通无向图,在这个图的基础上给出一些连边的操作,问每次操作后还剩多少个桥?
思路
首先跑一次tarjan不用说,这里尤注意与求scc的tarjan不同,关于桥的判断,要在dfs
结束回溯的时候直接if(low[v]>dfn[u])判断。
然后对于每一个操作,判断要加的两个点在不在一个双连通分量,
如果在一个双连通分量里面,不用进行任何操作(这样加边不会减少桥);
如果不在一个双连通分量里面,就会形成新的环,这个时候把两点之间的路径上的桥的标记全部去掉即可。
(注意代码中“边权下放”的思想)。
所谓“边权下放”的思想,我认为,是把本来以边为下标存储的信息变成以点为下标的思想。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
const int maxn=100000+10,maxm=200000+10;
stack<int> s;
vector<int> G[maxn];
//struct edge{
// int v,next;
//}a[maxm];
//int h[maxn],tot;
/*void add_e(int x,int y){
a[tot].v=y;
a[tot].next=h[x];
h[x]=tot++;
}*/
int dfn[maxn],low[maxn],isBri[maxn],belong[maxn],father[maxn];
int dfs_clock,cnt_bri;
//int f[maxn][25],dep[maxn];
void dfs(int u,int fa){
father[u]=fa;
low[u]=dfn[u]=++dfs_clock;
s.push(u);
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
// if(i/2==fa/2&&fa!=-1)continue;
if(v==fa)continue;
if(!dfn[v]){
// dfs(v,i);
dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]){
isBri[v]=1;//“边权下放”
cnt_bri++;
int x;
do{
x=s.top();s.pop();
belong[x]=cnt_bri;
}while(x!=v);
}
}else{
low[u]=min(low[u],dfn[v]);
}
}
}
/*void dfs2(int u,int fa){
dep[u]=dep[fa]+1;
f[u][0]=fa;
for(int i=1;i<=20;i++)
f[u][i]=f[f[u][i-1]][i-1];
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==fa)continue;
dfs2(v,u);
}
}*/
/*int lca(int x,int y){
if(dep[x]<dep[y])
swap(x,y);
int dc=dep[x]-dep[y];
for(int i=0;i<=20;i++){
if((1<<i)&dc)
x=f[x][i];
}
if(x==y)return x;
for(int i=20;i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}*/
/*void mark(int u,int goal){
while(u!=goal){
if(isBri[u]){
cnt_bri--;
isBri[u]=0;
}
u=f[u][0];
}
}*/
void func(int x,int y){
while(x!=y){
if(isBri[x]){
isBri[x]=0;
cnt_bri--;
}
if(isBri[y]){
isBri[y]=0;
cnt_bri--;
}
x=father[x];
y=father[y];
}
}
int main(){
// freopen("JJJ.in","r",stdin);
int n,m,kase=0;
while(scanf("%d%d",&n,&m)==2&&(n+m)){
kase++;
printf("Case %d:
",kase);
// tot=0;
// memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
G[i].clear();
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
// add_e(x,y);
// add_e(y,x);
G[x].push_back(y);
G[y].push_back(x);
}
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
memset(isBri,0,sizeof isBri);
memset(belong,0,sizeof belong);
dfs_clock=cnt_bri=0;
// dfs(1,-1);//tarjan
dfs(1,0);//tarjan
// dfs2(1,0);//lca
// printf("%d
",cnt_bri);
int k;
scanf("%d",&k);
while(k--){
int x,y;
scanf("%d%d",&x,&y);
if(belong[x]!=belong[y]){
// int anc=lca(x,y);
// mark(x,anc);
// mark(y,anc);
func(x,y);
}
printf("%d
",cnt_bri);
}
printf("
");
}
return 0;
}
后记
此题巨坑,由于N的范围太大,导致写lca的时候树上倍增会MLE,所以这里要写暴力的。
但是我感觉我的暴力lca好像有点错了,不过莫名其妙地也A了。
有空改一下。