题解:SP10931 ONBRIDGE - Online Bridge Searching
割边和桥都只一条若被删去则原图不连通的边。题意很明了,但是如果按照题意模拟,每加一次边就暴力求桥,显然会有无法接受的复杂度。考虑进行优化。
思路
我们先以加入的时间为权值做原图的最小生成树。注意到若一条边在某段时间里对答案有正贡献,其一定是在该生成树中的,而不在此生成树中的边只可能会有负贡献。
对答案有正贡献的边可以用类似 kruskal 的做法,不需要进行排序(因为加入边是按照时间顺序加入的),直接并查集维护,若当前加入的边的两个端点不在同一连通块内即为生成树的某条边。
考虑如何计算负贡献。记 \(u,v\) 为不在生成树中的某条边的两个端点。其必然连通树上的某两个点(否则该边必然在生成树中),此时原树变为基环树,除环外其他的边仍是割边。而该环上一定有三个点,分别是 \(u\),\(v\),和 \(u,v\) 的最近公共祖先(LCA)。该环上除当前边的所有边在原图中均为割边。加入当前边后环上所有边均不是割边,故减去。
事实上我们在求出最小生成树后不会进行任何的加边操作,写成基环树只是方便理解,树还是原树。割边不计算贡献也不需要删边,打上标记就好了。
在实现过程中,我们可以用倍增法求出 \(u,v\) 的最近公共祖先,并暴力的分别从 \(u\) 和 \(v\) 往上跳并统计边数,同时还要取消它们割边的标记,以防重复计算。因为时间限制给的很抽象,所以尽量不要偷懒用 map
维护两个端点来得到边的编号。我就是这样偷懒被卡掉了。直接标记以某个点为深度较大的那条边是否为割边即可。
码风似乎不太好,见谅。
code:
int t;
int n,m;
class node{
public:
int u;
int v;
};
node ee[1000086];
int eg;
std::vector<int>e[1000086];
int ff[1000086];
int fa[1000086][30];
int dis[1000086];
bool flag[1000086];
bool flag2[1000086];
int find(int x){
return ff[x]==x?x:ff[x]=find(ff[x]);
}
void add(int x,int y){
x=find(x);
y=find(y);
if(x!=y)
ff[x]=y;
return;
}
void dfs(int x,int fath){
for(int j=1;j<=25;j++)
fa[x][j]=fa[fa[x][j-1]][j-1];//lca 的预处理
for(int i:e[x])
if(i!=fa[x][0] and (not dis[i])){
fa[i][0]=x;
dis[i]=dis[x]+1;
flag2[i]=true;//在生成树里的所有点都会有贡献,打上标记
dfs(i,x);
}
return;
}
int lca(int x,int y){//lca 板子
if(dis[x]<dis[y])
std::swap(x,y);
for(int i=25;i>=0;i--)
if(dis[x]-(1LL<<i)>=dis[y])
x=fa[x][i];
if(x==y)
return x;
for(int i=25;i>=0;i--)
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
int main(){
std::cin>>t;//多测
while(t--){
std::cin>>n>>m;
int ans=0;
for(int i=0;i<=n;i++){
ff[i]=i;
e[i].clear();
}
memset(flag,false,sizeof flag);
memset(dis,0,sizeof dis);
memset(fa,0,sizeof fa);
memset(flag2,false,sizeof flag2);//千万不要忘记初始化!
for(int i=1;i<=m;i++){
int x,y;
std::cin>>x>>y;
if(find(x)!=find(y)){//并查集维护一下
flag[i]=true;//标记当前点为割边
e[x].push_back(y);
e[y].push_back(x);//建图
add(x,y);//合并
}
ee[i]=(node){x,y};//存边
}
dfs(0,-1);//做倍增求 lca 的预处理
for(int i=1;i<=m;i++){
if(flag[i])
ans++;
else{
int qwq=lca(ee[i].u,ee[i].v);
for(int j=ee[i].u;j!=qwq;j=fa[j][0]){
if(flag2[j])
ans--;
flag2[j]=false;
}
for(int j=ee[i].v;j!=qwq;j=fa[j][0]){
if(flag2[j])
ans--;
flag2[j]=false;
}//暴力往上跳
}
std::cout<<ans<<"\n";
}
}
return 0;//撒花~
}
好像不是最优解,%%%最优解@LLCCFF