公元 8192 年,人类进入星际大航海时代。在不懈的努力之下,人类占领了
宇宙中的 n 个行星,并在这些行星之间修建了 n - 1 条星际航道,使得任意两个
行星之间可以通过唯一的一条路径互相到达。
同时,在宇宙中还有一些空间跳跃点,有些跳跃点已经被发现,还有一些是
未知的,每个跳跃点连接了两个行星,使得这两个行星中的任意一个都可以通过
这个跳跃点到达另外一个行星。这些跳跃点因为充斥着巨大的能量,所以它们很
容易崩溃。
宇宙中还有一些意图毁灭人类的外星人,他们派出了一些使者潜伏在行星
上。现在这些使者想要离开他们各自藏身的行星u,到其他行星 v 去搜集情报。
由于这些使者十分小心, 他们不会经过任意一条属于这两个行星的路径上的星际
航道(即不会走在 u 到 v 路径上的星际航道) 。这样他们就只能借助一些跳跃点
来到达目的地,但是这些外星人的身体十分脆弱,所以他们只能通过最多一个跳
跃点。
现在告诉你若干个按照时间顺序给出的事件,每个事件可能是一个跳跃点又
被发现了,也可能是一个跳跃点崩溃了,还有可能是一个外星使者想离开行星u
到行星v去。
请问每个外星使者有多少条不同的路径可以选择?
对于100%的数据,n ≤ 105,m ≤ 5 × 104,q ≤ 5 × 104 数据保证x不等于y
纯粹为了练码速,结果到最后调傻了。。
对于一条边(x,y),若x和y非祖先的关系,那么只有x子树内的点走到y子树内的点才会产生1的贡献
若x为y的祖先,z为x到y链上非y的最浅点,那么只有x子树内的点走到z子树补集内的点才会产生1的贡献
乍一看这是一个区间矩形修改+单点查询的树套树题目,实际上如果我们把非树边(x,y)看成坐标为(dfn[x],dfn[y])的二维平面上的点,那么查询(a,b)等价于按照上面的方法查询矩形和,修改就变成了单点修改了
然后树状数组套线段树就可以了,空间够随便开
打错了一点地方调了很久GG,老年选手.jpg
#include
#include
#include
#include
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define lowbit(x) (x&(-x))
const int N=100005;
struct treeNode {int l,r,sum;} t[N*401];
struct edge {int y,next;} e[N*2];
int pos[N],dep[N],fa[N],bl[N],size[N],n;
int root[N*2],ls[N],edCnt,tot;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void add_edge(int x,int y) {
e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}
void modify(int &now,int tl,int tr,int x,int v) {
if (!now) now=++tot;
t[now].sum+=v;
if (tl==tr) return ;
int mid((tl+tr)>>1);
if (x<=mid) modify(t[now].l,tl,mid,x,v);
else modify(t[now].r,mid+1,tr,x,v);
}
int query(int now,int tl,int tr,int l,int r) {
if (r<l||!now) return 0;
if (tl>=l&&tr<=r) return t[now].sum;
int mid((tl+tr)>>1);
int qx=query(t[now].l,tl,mid,l,std:: min(r,mid));
int qy=query(t[now].r,mid+1,tr,std:: max(mid+1,l),r);
return qx+qy;
}
void change(int x,int p,int v) {
for (;x<=pos[0];x+=lowbit(x)) modify(root[x],1,pos[0],p,v);
}
int get(int x,int l,int r) {
int res=0;
for (;x;x-=lowbit(x)) res+=query(root[x],1,pos[0],l,r);
return res;
}
int ask(int x,int y,int l,int r) {
if (x>y) return 0;
return get(y,l,r)-get(x-1,l,r);
}
void dfs1(int now) {
size[now]=1;
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y==fa[now]) continue;
fa[e[i].y]=now; dep[e[i].y]=dep[now]+1;
dfs1(e[i].y); size[now]+=size[e[i].y];
}
}
void dfs2(int now,int up) {
bl[now]=up; pos[now]=++pos[0];
int mx=0;
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y!=fa[now]&&size[e[i].y]>size[mx]) mx=e[i].y;
}
if (!mx) return ;
dfs2(mx,up);
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y!=fa[now]&&e[i].y!=mx) dfs2(e[i].y,e[i].y);
}
}
int get_lca(int x,int y) {
for (;bl[x]!=bl[y];) {
if (dep[bl[x]]<dep[bl[y]]) std:: swap(x,y);
x=fa[bl[x]];
}
return dep[x]<dep[y]?x:y;
}
int get_up(int x,int y) {
if (fa[x]==y) return x;
for (;bl[x]!=bl[y];) {
if (fa[bl[x]]==y) return bl[x];
x=fa[bl[x]];
}
for (int i=ls[y];i;i=e[i].next) {
if (e[i].y!=fa[y]&&bl[e[i].y]==bl[y]) return e[i].y;
}
}
int main(void) {
n=read();
rep(i,2,n) add_edge(read(),read());
dfs1(1); dfs2(1,1);
int m=read();
rep(i,1,m) {
int x=read(),y=read();
if (pos[x]>pos[y]) std:: swap(x,y);
change(pos[x],pos[y],1);
}
for (int T=read();T--;) {
int opt=read(),x=read(),y=read();
if (pos[x]>pos[y]) std:: swap(x,y);
if (opt==1) change(pos[x],pos[y],1);
else if (opt==2) change(pos[x],pos[y],-1);
else {
int lca=get_lca(x,y),ans=0;
if (lca==x) {
int up=get_up(y,x);
ans=ask(pos[y],pos[y]+size[y]-1,pos[up]+size[up],pos[0]);
ans+=ask(1,pos[up]-1,pos[y],pos[y]+size[y]-1);
} else ans=ask(pos[x],pos[x]+size[x]-1,pos[y],pos[y]+size[y]-1);
printf("%d\n", ans);
}
}
return 0;
}