bzoj4285 使者 树状数组套线段树

Description


公元 8192 年,人类进入星际大航海时代。在不懈的努力之下,人类占领了
宇宙中的 n 个行星,并在这些行星之间修建了 n - 1 条星际航道,使得任意两个
行星之间可以通过唯一的一条路径互相到达。
同时,在宇宙中还有一些空间跳跃点,有些跳跃点已经被发现,还有一些是
未知的,每个跳跃点连接了两个行星,使得这两个行星中的任意一个都可以通过
这个跳跃点到达另外一个行星。这些跳跃点因为充斥着巨大的能量,所以它们很
容易崩溃。
宇宙中还有一些意图毁灭人类的外星人,他们派出了一些使者潜伏在行星
上。现在这些使者想要离开他们各自藏身的行星u,到其他行星 v 去搜集情报。
由于这些使者十分小心, 他们不会经过任意一条属于这两个行星的路径上的星际
航道(即不会走在 u 到 v 路径上的星际航道) 。这样他们就只能借助一些跳跃点
来到达目的地,但是这些外星人的身体十分脆弱,所以他们只能通过最多一个跳
跃点。
现在告诉你若干个按照时间顺序给出的事件,每个事件可能是一个跳跃点又
被发现了,也可能是一个跳跃点崩溃了,还有可能是一个外星使者想离开行星u
到行星v去。
请问每个外星使者有多少条不同的路径可以选择?

对于100%的数据,n ≤ 105,m ≤ 5 × 104,q ≤ 5 × 104 数据保证x不等于y

Solution


纯粹为了练码速,结果到最后调傻了。。
对于一条边(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

Code


#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;
}

你可能感兴趣的:(c++,树套树)