测试地址:航线规划
做法:本题需要用到边双连通分量+树链剖分。
注意到,这题有删边操作,又要维护边双连通分量相关信息(从割边自然想到维护边双连通分量),无法维护。注意到没有强制在线,所以我们可以倒过来,变成有加边操作,同时维护边双连通分量,这样就好做了。
首先将边双连通分量缩点,缩完后图变为一棵树,而每加一条边,实际上就是把两个端点在树中的路径合并为一个点。虽然维护合并貌似可以用并查集暴力维护(因为总的合并次数不超过),但是两点间的割边数量就没法求了。于是我们不能实际上做“缩点”这一操作,而是把对应路径上的边权都变成(默认原先路径上边权都是),这样两点间路径的和就是它们之间的割边数量了。实际上,这个边权的意义在于,如果两个端点属于同一个边双连通分量,这条边就不是割边,边权就是,否则它就是割边,边权就是。这样一来,用树链剖分维护即可,时间复杂度为(为操作次数)。
这题貌似有挺多做法,有一种不用Tarjan,而是用并查集缩点的方法,应该可行。而另一种方法,是在最后还存在的边集中求出一棵生成树,然后暴力加边,看似不用缩点,简单而又非常可做,但要注意的是,这样的时间复杂度是的,时限紧的话有可能会被卡掉。
我傻逼的地方:求边双连通分量的算法写错了……一定要记住这几个Tarjan算法的区别……
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,t,td,c[40010],x[40010],y[40010],ans[40010];
int first[30010]={0},tot=0,totbcc;
int dfn[30010],low[30010],belong[30010],tim=0,st[30010],top=0;
int fa[30010],dep[30010],son[30010]={0},siz[30010];
int pos[30010],postim[30010],tp[30010];
int seg[120010],tag[120010]={0};
bool vis[30010]={0};
struct forsort
{
int a,b;
}p[200010],d[80010];
struct edge
{
int v,next;
}e[200010];
bool cmp(forsort a,forsort b)
{
if (a.a==b.a) return a.b<b.b;
else return a.a<b.a;
}
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
}
void init()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&p[2*i-1].a,&p[2*i-1].b);
p[2*i].a=p[2*i-1].b;
p[2*i].b=p[2*i-1].a;
}
sort(p+1,p+2*m+1,cmp);
t=td=0;
while(scanf("%d",&c[++t])&&c[t]!=-1)
{
scanf("%d%d",&x[t],&y[t]);
if (!c[t])
{
d[++td].a=x[t],d[td].b=y[t];
d[++td].a=y[t],d[td].b=x[t];
}
}
sort(d+1,d+td+1,cmp);
int x=1;
for(int i=1;i<=2*m;i++)
{
while(x<td&&(d[x].a<p[i].a||(d[x].a==p[i].a&&d[x].b<p[i].b))) x++;
if (d[x].a==p[i].a&&d[x].b==p[i].b) continue;
insert(p[i].a,p[i].b);
}
}
void dfs(int v,int fa)
{
dfn[v]=low[v]=++tim;
st[++top]=v;
vis[v]=1;
int now=top;
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa)
{
if (!vis[e[i].v])
{
dfs(e[i].v,v);
low[v]=min(low[v],low[e[i].v]);
}
else low[v]=min(low[v],dfn[e[i].v]);
}
if (low[v]==dfn[v])
{
++totbcc;
while(top>=now)
{
belong[st[top]]=totbcc;
top--;
}
}
}
void tarjan()
{
totbcc=0;
dfs(1,0);
memset(first,0,sizeof(first));
tot=0;
int x=1;
for(int i=1;i<=2*m;i++)
{
while(x<td&&(d[x].a<p[i].a||(d[x].a==p[i].a&&d[x].b<p[i].b))) x++;
if (d[x].a==p[i].a&&d[x].b==p[i].b) continue;
if (belong[p[i].a]!=belong[p[i].b])
insert(belong[p[i].a],belong[p[i].b]);
}
}
void dfs1(int v)
{
siz[v]=1;
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa[v])
{
fa[e[i].v]=v;
dep[e[i].v]=dep[v]+1;
dfs1(e[i].v);
if (!son[v]||siz[son[v]]<siz[e[i].v])
son[v]=e[i].v;
siz[v]+=siz[e[i].v];
}
}
void dfs2(int v,int top)
{
tp[v]=top;
postim[++tim]=v;
pos[v]=tim;
if (son[v]) dfs2(son[v],top);
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa[v]&&e[i].v!=son[v])
dfs2(e[i].v,e[i].v);
}
void pushdown(int no)
{
if (tag[no])
{
tag[no<<1]=tag[no<<1|1]=tag[no];
seg[no<<1]=seg[no<<1|1]=0;
tag[no]=0;
}
}
void pushup(int no)
{
seg[no]=seg[no<<1]+seg[no<<1|1];
}
void buildtree(int no,int l,int r)
{
if (l==r) {seg[no]=1;return;}
int mid=(l+r)>>1;
buildtree(no<<1,l,mid);
buildtree(no<<1|1,mid+1,r);
pushup(no);
}
void segmodify(int no,int l,int r,int s,int t)
{
if (l>=s&&r<=t)
{
tag[no]=1;
seg[no]=0;
return;
}
int mid=(l+r)>>1;
pushdown(no);
if (s<=mid) segmodify(no<<1,l,mid,s,t);
if (t>mid) segmodify(no<<1|1,mid+1,r,s,t);
pushup(no);
}
int segquery(int no,int l,int r,int s,int t)
{
if (l>=s&&r<=t) return seg[no];
int mid=(l+r)>>1,sum=0;
pushdown(no);
if (s<=mid) sum+=segquery(no<<1,l,mid,s,t);
if (t>mid) sum+=segquery(no<<1|1,mid+1,r,s,t);
return sum;
}
int query(int x,int y)
{
int ans=0;
while(tp[x]!=tp[y])
{
if (dep[tp[x]]<dep[tp[y]]) swap(x,y);
ans+=segquery(1,1,totbcc,pos[tp[x]],pos[x]);
x=fa[tp[x]];
}
if (dep[x]>dep[y]) swap(x,y);
if (x!=y) ans+=segquery(1,1,totbcc,pos[x]+1,pos[y]);
return ans;
}
void modify(int x,int y)
{
while(tp[x]!=tp[y])
{
if (dep[tp[x]]<dep[tp[y]]) swap(x,y);
segmodify(1,1,totbcc,pos[tp[x]],pos[x]);
x=fa[tp[x]];
}
if (dep[x]>dep[y]) swap(x,y);
if (x!=y) segmodify(1,1,totbcc,pos[x]+1,pos[y]);
}
void work()
{
buildtree(1,1,totbcc);
t--;
for(int i=t;i>=1;i--)
{
if (c[i]) ans[i]=query(belong[x[i]],belong[y[i]]);
else modify(belong[x[i]],belong[y[i]]);
}
for(int i=1;i<=t;i++)
if (c[i]) printf("%d
",ans[i]);
}
int main()
{
init();
tarjan();
fa[n+1]=dep[n+1]=0;
dfs1(1);
tim=0;
dfs2(1,1);
work();
return 0;
}