题目:
题解:
一看这种断边的就知道要【倒悬并查集】,倒序加边
因为时刻保证任意两点相连,那他起码是一棵树,我们倒序加边,加成一棵树之后,每增加一条非树边,就意味着在这条边的两端点的路径上+1,询问的时候只需要统计路径上有多少为0的边
思路还是挺好想,但是在我写的时候有一些小问题:对于这种边权下放为点权的,in[x]+1和in[son[x]]不相等,因为可能没有重儿子,当两个点重合并都为叶子节点的时候就凉了
代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int N=30005;
const int M=100005;
const int A=40005;
struct hh{int x,y,id;}a[M],ask[A];
int n,tot,yx,nxt[N*2],point[N],v[N*2],tot1,nxt1[N*2],point1[N],v1[N*2],c[N*2],f[N],size[N],h[N],son[N],top[N],in[N],num,cnt,answer[A],sum[N*4],delta[N*4];
bool xz[M],vis[M];
int find(int x)
{
if (f[x]!=x) f[x]=find(f[x]);
return f[x];
}
void addline(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void addline1(int x,int y,int id){++tot1; nxt1[tot1]=point1[x]; point1[x]=tot1; v1[tot1]=y; c[tot1]=id;}
void dfs_1(int x,int fa)
{
f[x]=fa; size[x]=1; h[x]=h[fa]+1;int maxx=0;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
dfs_1(v[i],x);
size[x]+=size[v[i]];
if (maxx<size[v[i]]) maxx=size[v[i]],son[x]=v[i];
}
}
void dfs_2(int x,int fa)
{
if (son[fa]!=x) top[x]=x;
else top[x]=top[fa];
in[x]=++yx;
if (son[x])
{
dfs_2(son[x],x);
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa && v[i]!=son[x]) dfs_2(v[i],x);
}
}
void updata(int now){sum[now]=sum[now<<1]+sum[now<<1|1];}
void build(int now,int l,int r)
{
delta[now]=-1;
if (l==r)
{
sum[now]=1;
if (l==1) sum[now]=0;
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
updata(now);
}
void pushdown(int now)
{
if (delta[now]==0)
{
delta[now<<1]=delta[now<<1|1]=0;
sum[now<<1]=sum[now<<1|1]=0;
delta[now]=-1;
}
}
void change(int now,int l,int r,int lrange,int rrange)
{
if (lrange<=l && rrange>=r){sum[now]=0; delta[now]=0; return;}
int mid=(l+r)>>1;pushdown(now);
if (lrange<=mid) change(now<<1,l,mid,lrange,rrange);
if (rrange>mid) change(now<<1|1,mid+1,r,lrange,rrange);
updata(now);
}
int qurry(int now,int l,int r,int lrange,int rrange)
{
if (lrange<=l && rrange>=r) return sum[now];
int mid=(l+r)>>1,ans=0;pushdown(now);
if (lrange<=mid) ans+=qurry(now<<1,l,mid,lrange,rrange);
if (rrange>mid) ans+=qurry(now<<1|1,mid+1,r,lrange,rrange);
return ans;
}
void Chan(int u,int v,int id)
{
int f1=top[u],f2=top[v],ans=0;
while (f1!=f2)
{
if (h[f1]<h[f2]) swap(f1,f2),swap(u,v);
if (id==1) change(1,1,n,in[f1],in[u]);
else ans+=qurry(1,1,n,in[f1],in[u]);
u=f[f1]; f1=top[u];
}
if (in[u]>in[v]) swap(u,v);
if (id==1) change(1,1,n,in[u]+1,in[v]);//////
else
{
ans+=qurry(1,1,n,in[u]+1,in[v]);
answer[++cnt]=ans;
}
}
int main()
{
int m;scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
if (a[i].x>a[i].y) swap(a[i].x,a[i].y);
addline1(a[i].x,a[i].y,i);
}
int id,x,y;
while (1)
{
scanf("%d",&id);if (id==-1) break;
if (id==1)
{
scanf("%d%d",&x,&y);
ask[++num].x=x; ask[num].y=y; ask[num].id=1;
}
else
{
scanf("%d%d",&x,&y);
if (x>y) swap(x,y);
ask[++num].x=x; ask[num].y=y; ask[num].id=0;
for (int i=point1[x];i;i=nxt1[i])
if (v1[i]==y) {xz[c[i]]=1;break;}
}
}
int bs=0;
for (int i=1;i<=n;i++) f[i]=i;
for (int i=1;i<=m;i++)
if (!xz[i])
{
int x=find(a[i].x),y=find(a[i].y);
if (x!=y)
{
f[x]=y;vis[i]=1;
addline(a[i].x,a[i].y);
bs++;if (bs==n-1) break;
}
}
dfs_1(1,0);
dfs_2(1,0);
build(1,1,n);
for (int i=1;i<=m;i++)
if (!xz[i] && !vis[i]) Chan(a[i].x,a[i].y,1);
for (int i=num;i>=1;i--)
if (ask[i].id==1) Chan(ask[i].x,ask[i].y,0);
else Chan(ask[i].x,ask[i].y,1);
for (int i=cnt;i>=1;i--) printf("%d\n",answer[i]);
}