题解
对于这样一类删边+询问的题目,可以很快就想到“正难则反”这一思路,因此可以离线做,先将要删的边删掉,再反着处理每一个询问,于是删边就被处理成了加边。
然而,即使这样,仍然很麻烦,因为这是一个图,可能有环,非常麻烦。
可以考虑运用dfs树,先利用dfs造出一棵树,然后再将没包含在树上的边加入更新。
对于关键路径,非常显然,就是树上的边了。
现在考虑如何更新。
每加入一条边,就在树上形成了一个环,将这个环上的边全缩成一个点,就可以了,对于缩点,很快就可以想到利用并查集。
询问时,不断利用并查集向上跳,一直到LCA,这样虽然不是正解,但是已经可以过掉这题了。
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
using namespace std;
const int M=30005;
vector<int>edge[M];
int n,m,q,a[40005],b[40005],c[40005];
bool mark[M];
int fa[M],dep[M];
void rec(int x){
mark[x]=1;
for(int i=0;i<edge[x].size();i++){
if(mark[edge[x][i]]) continue;
fa[edge[x][i]]=x;
dep[edge[x][i]]=dep[x]+1;
rec(edge[x][i]);
}
}
int par[M],ans[40005];
int get(int x){
if(par[x]!=x) par[x]=get(par[x]);
return par[x];
}
int Query(int a,int b){
int ans=0;
a=get(a);b=get(b);
while(a!=b){
if(dep[a]<dep[b]) swap(a,b);
a=get(fa[a]);
ans++;
}
return ans;
}
void update(int a,int b){
a=get(a);
b=get(b);
while(a!=b){
if(dep[a]<dep[b]) swap(a,b);
par[a]=fa[a];
a=get(a);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
edge[a].push_back(b);
edge[b].push_back(a);
}
for(int i=1;i<=n;i++)
par[i]=i;
for(int i=1;i<=n;i++)
sort(edge[i].begin(),edge[i].end());
while(true){
scanf("%d",&a[q]);
if(a[q]==-1) break;
scanf("%d%d",&b[q],&c[q]);
if(a[q]==0){
edge[b[q]].erase(lower_bound(edge[b[q]].begin(),edge[b[q]].end(),c[q]));
edge[c[q]].erase(lower_bound(edge[c[q]].begin(),edge[c[q]].end(),b[q]));
}
q++;
}
rec(1);
fa[1]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<edge[i].size();j++){
if(fa[i]==edge[i][j]||fa[edge[i][j]]==i) continue;
update(edge[i][j],i);
}
}
for(int i=q-1;i>=0;i--){
if(a[i]==0){
update(b[i],c[i]);
}else{
ans[i]=Query(b[i],c[i]);
}
}
for(int i=0;i<q;i++)
if(a[i]) printf("%d\n",ans[i]);
return 0;
}
正解还是非常机智的。
考虑一棵树,如果把一条树边删掉,那么它的子树深度就会全部减少一。根据两个点的深度以及两个点的LCA就可以求出这两个点之间的树边的条数。
首先利用dfs序将问题转换到序列上,因为在dfs序中,子树是一段连续的区间。
于是可以利用一个树状数组来维护,这棵树状数组是区间更新,单点询问的。
其余的和上面是一样的。
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
#define lowbit(x) (x&-x)
using namespace std;
const int M=30005;
vector<int>edge[M];
int n,m,q,a[40005],b[40005],c[40005];
bool mark[M];
int fa[M][20],dep[M],dfn[M],dfc,mx[M];
void rec(int x){
mark[x]=1;
dfn[x]=++dfc;
for(int i=0;i<edge[x].size();i++){
if(mark[edge[x][i]]) continue;
fa[edge[x][i]][0]=x;
dep[edge[x][i]]=dep[x]+1;
rec(edge[x][i]);
}
mx[x]=dfc;
}
int par[M],ans[40005];
int get(int x){
if(par[x]!=x) par[x]=get(par[x]);
return par[x];
}
int bit[M];
void add(int x,int w){
for(int i=x;i>0;i-=lowbit(i))
bit[i]+=w;
}
int sum(int x){
int res=0;
for(int i=x;i<=dfc;i+=lowbit(i))
res+=bit[i];
return res;
}
int up(int a,int step){
for(int i=0;i<20;i++){
if(step&(1<<i)) a=fa[a][i];
}
return a;
}
int LCA(int a,int b){
if(dep[a]<dep[b]) swap(a,b);
a=up(a,dep[a]-dep[b]);
if(a!=b){
for(int i=19;i>=0;i--){
if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
}
a=fa[a][0];
}
return a;
}
int Query(int a,int b){
return sum(dfn[a])+sum(dfn[b])-sum(dfn[LCA(a,b)])*2;
}
void del(int a,int rt){
a=get(a);
while(dep[a]>dep[rt]){
add(dfn[a]-1,1);
add(mx[a],-1);
par[a]=fa[a][0];
a=get(a);
}
}
void update(int a,int b){
int lca=get(LCA(a,b));
del(a,lca);
del(b,lca);
}
inline void Rd(int&res){
res=0;char c;
while(c=getchar(),!isdigit(c));
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),isdigit(c));
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
edge[a].push_back(b);
edge[b].push_back(a);
}
for(int i=1;i<=n;i++)
par[i]=i;
for(int i=1;i<=n;i++)
sort(edge[i].begin(),edge[i].end());
for(int i=0;i<q;i++){
Rd(a[i]);Rd(b[i]);Rd(c[i]);
if(a[i]==0){
edge[b[i]].erase(lower_bound(edge[b[i]].begin(),edge[b[i]].end(),c[i]));
edge[c[i]].erase(lower_bound(edge[c[i]].begin(),edge[c[i]].end(),b[i]));
}
}
rec(1);
for(int i=1;i<=n;i++){
add(dfn[i],dep[i]);
add(dfn[i]-1,-dep[i]);
}
for(int j=1;j<20;j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
for(int i=1;i<=n;i++){
for(int j=0;j<edge[i].size();j++){
if(fa[i][0]==edge[i][j]||fa[edge[i][j]][0]==i) continue;
update(edge[i][j],i);
}
}
for(int i=q-1;i>=0;i--){
if(a[i]==0){
update(b[i],c[i]);
}else{
ans[i]=Query(b[i],c[i]);
}
}
for(int i=0;i<q;i++)
if(a[i]) printf("%d\n",ans[i]);
return 0;
}