暂无链接
航线规划
【问题描述】
对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel
星球所在的星系——一个巨大的由千百万星球构成的Samuel星系。 星际空间站的Samuel
II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些
星球从1开始编号1、2、3……。 一些先遣飞船已经出发,在星球之间开辟探险航线。
探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到1号星
球也可以使用这条航线。
例如下图所示:
在5个星球之间,有5条探险航线。 A、B两星球之间,如果某条航线不存在,就无法从A星球抵达B星球,我们则称这条航线为关键航线。 显然上图中,1号与5号星球之间的关键航线有1条:即为4-5航线。 然而,在宇宙中一些未知的磁暴和行星的冲撞,使得已有的某些航线被破坏,随着越来越多的航线被破坏,探险飞船又不能及时回复这些航线,可见两个星球之间的关键航线会越来越多。 假设在上图中,航线4-2(从4号星球到2号星球)被破坏。此时,1号与5号星球之间的关键航线就有3条:1-3,3-4,4-5。
小联的任务是,不断关注航线被破坏的情况,并随时给出两个星球之间的关键航线数目。现在请你帮助完成。
【输入格式】
第一行有两个整数N,M。表示有N个星球,初始时已经有M条航线。随后有M行,每行有两个不相同的整数A、B表示在星球A与B之间存在一条航线。接下来每行有三个整数C、A、B。C为1表示询问当前星球A和星球B之间有多少条关键航线;C为0表示在星球A和星球B之间的航线被破坏,当后面再遇到C为1的情况时,表示询问航线被破坏后,关键路径的情况,且航线破坏后不可恢复; C为-1表示输入文件结束,这时该行没有A,B的值。
【输出格式】
对每个C为1的询问,输出一行一个整数表示关键航线数目。 注意:我们保证无论
航线如何被破坏,任意时刻任意两个星球都能够相互到达。在整个数据中,任意两个星球
之间最多只可能存在一条直接的航线。
【输入样例】
5 5
1 2
1 3
3 4
4 5
4 2
1 1 5
0 4 2
1 5 1
-1
【输出样例】
1
3
【数据范围】
对于30%的数据:1 ≤ N ≤ 20,1 ≤ M ≤ 35,1 ≤ 查询次数+破坏航线数目 ≤ 100
对于50%的数据:1 ≤ N ≤ 5000,1 ≤ M ≤ 5300,1 ≤ 查询次数+破坏航线数目 ≤ 10000
对于100%的数据:1 ≤ N ≤ 30000,1 ≤ M ≤ 100000,1 ≤ 查询次数+破坏航线次数 ≤ 40000
题解
忘了拆点会让点数翻倍,少开了一半数组, 分惨变 分。
思路很简单,一看就需要离线,当图变成树的时候,所有边都是关键路径,倒着往里面加边的时候,每形成一个环,那么环上的所有路径都可以被这条路径在环上的补集替换,即整个环上的边都不再是关键路径了。
直接大力 维护,把边换成点权为 的点,成环的时候将整条链的点赋值为 , 维护一下点权和就可以求出链上的关键路径数了。
代码
#include<bits/stdc++.h>
#define ls son[v][0]
#define rs son[v][1]
using namespace std;
const int M=3e5+5;
struct sd{int op,a,b,id;}ope[M];
set<int>mmp[M];
int tot,n,m,id,son[M][2],dad[M],val[M],sum[M],ans[M];
bool rev[M],zero[M];
bool notroot(int v){return son[dad[v]][0]==v||son[dad[v]][1]==v;}
void up(int v){sum[v]=sum[ls]+sum[rs]+val[v];}
void turn(int v){swap(ls,rs),rev[v]^=1;}
void dan(int v){val[v]=sum[v]=0;zero[v]=1;}
void push(int v)
{
if(rev[v]){if(ls)turn(ls);if(rs)turn(rs);rev[v]=0;}
if(zero[v]){if(ls)dan(ls);if(rs)dan(rs);zero[v]=0;}
}
void down(int v){if(notroot(v))down(dad[v]);push(v);}
void spin(int v)
{
int f=dad[v],ff=dad[f],k=son[f][1]==v,w=son[v][!k];
if(notroot(f))son[ff][son[ff][1]==f]=v;son[v][!k]=f,son[f][k]=w;
if(w)dad[w]=f;dad[f]=v,dad[v]=ff;
up(f);up(v);
}
void splay(int v)
{
down(v);int f,ff;
while(notroot(v))
{
f=dad[v],ff=dad[f];
if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);
spin(v);
}
up(v);
}
void access(int v){for(int f=0;v;v=dad[f=v])splay(v),rs=f,up(v);}
void beroot(int v){access(v),splay(v),turn(v);}
int root(int v){access(v),splay(v);while(ls)push(v),v=ls;return v;}
void split(int x,int y){beroot(x),access(y),splay(y);}
void link(int x,int y){beroot(x);dad[x]=y;}
void in()
{
int a,b,c;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&a,&b);
if(a>b)swap(a,b);
mmp[a].insert(b);
}
for(tot=1;;++tot)
{
scanf("%d",&a);if(a==-1)break;
scanf("%d%d",&b,&c);
if(!a)
{
if(b>c)swap(b,c);
mmp[b].erase(c);
}
ope[tot]=(sd){a,b,c,tot};
}
--tot;
}
void ac()
{
memset(ans,-1,sizeof(ans));
set<int>::iterator j;id=n;
for(int i=1;i<=n;++i)
for(j=mmp[i].begin();j!=mmp[i].end();++j)
{
beroot(i);
if(root(*j)!=i)
{
++id;
val[id]=sum[id]=1;
link(i,id);link(id,*j);
}
else split(i,*j),dan(*j);
}
for(int i=tot;i>=1;--i)
{
if(ope[i].op)split(ope[i].a,ope[i].b),ans[ope[i].id]=sum[ope[i].b];
else split(ope[i].a,ope[i].b),dan(ope[i].b);
}
for(int i=1;i<=tot;++i)if(~ans[i])printf("%d\n",ans[i]);
}
int main(){in();ac();}