bzoj4771 七彩树 可持久化线段树+set

25 篇文章 0 订阅

Description


给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点。每个节点都被染上了某一种颜色,其中第i个节
点的颜色为c[i]。如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色。定义depth[i]为i节点与根节点的距离
,为了方便起见,你可以认为树上相邻的两个点之间的距离为1。站在这棵色彩斑斓的树前面,你将面临m个问题。
每个问题包含两个整数x和d,表示询问x子树里且depth不超过depth[x]+d的所有点中出现了多少种本质不同的颜色
。请写一个程序,快速回答这些询问。

Solution


之前做过类似的题目吧

一个颜色的贡献是1,我们用set维护dfs序,插入的时候减去dfs序相邻点lca的贡献,就变成二维数点问题了。
没有修改所有按深度建可持久化线段树就可以了
求lca要rmq不然会多一个log(虽然多一个log好像也能跑得过

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <set>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define fi first
#define se second

typedef std:: pair <int,int> pair;
const int N=200005;

struct edge {int y,next;} e[N*2];
struct treeNode {int l,r,sum;} t[N*81];

std:: set <pair> set[N];

int fa[N],dep[N],pos[N],size[N];
int rt[N],rec[N],mn[21][N],c[N];
int ls[N],p[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;
}

void modify(int &now,int pre,int tl,int tr,int x,int v) {
	t[now=++tot]=t[pre]; t[now].sum+=v;
	if (tl==tr) return ;
	int mid=(tl+tr)>>1;
	if (x<=mid) modify(t[now].l,t[pre].l,tl,mid,x,v);
	else modify(t[now].r,t[pre].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 dfs(int x) {
	pos[x]=++pos[0];
	mn[0][++rec[0]]=x;
	rec[x]=rec[0];
	size[x]=1;
	for (int i=ls[x];i;i=e[i].next) {
		dep[e[i].y]=dep[x]+1;
		dfs(e[i].y); size[x]+=size[e[i].y];
		mn[0][++rec[0]]=x;
	}
}

int get_lca(int x,int y) {
	x=rec[x],y=rec[y];
	if (x>y) std:: swap(x,y);
	int lg=log2(y-x+1);
	int qx=mn[lg][x],qy=mn[lg][y-(1<<lg)+1];
	return (dep[qx]<dep[qy])?qx:qy;
}

void clr() {
	fill(ls,0); edCnt=tot=0;
	fill(pos,0); fill(size,0);
	fill(rec,0); fill(dep,0);
	fill(rt,0);
}

bool cmp(int x,int y) {
	return dep[x]<dep[y];
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	int T=read();
	for (;T--;clr()) {
		int n=read(),m=read();
		rep(i,1,n) set[i].clear();
		rep(i,1,n) c[i]=read();
		rep(i,2,n) {
			fa[i]=read();
			add_edge(fa[i],i);
		}
		dfs(1);
		rep(j,1,20) {
			int len=1<<j-1;
			rep(i,1,rec[0]-len+1) {
				if (dep[mn[j-1][i]]<dep[mn[j-1][i+len]]) {
					mn[j][i]=mn[j-1][i];
				} else mn[j][i]=mn[j-1][i+len];
			}
		}
		rep(i,1,n) p[i]=i;
		std:: sort(p+1,p+n+1,cmp);
		rep(i,1,n) {
			int x=p[i];
			if (dep[x]!=dep[p[i-1]]) rt[dep[x]]=rt[dep[p[i-1]]];
			set[c[x]].insert(pair(pos[x],x));
			std:: set <pair>:: iterator it=set[c[x]].find(pair(pos[x],x));
			int nex=0,pre=0;
			it++; if (it!=set[c[x]].end()) nex=(*it).se; it--;
			if (it!=set[c[x]].begin()) it--,pre=(*it).se,it++;
			modify(rt[dep[x]],rt[dep[x]],1,n,pos[x],1);
			if (pre) modify(rt[dep[x]],rt[dep[x]],1,n,pos[get_lca(x,pre)],-1);
			if (nex) modify(rt[dep[x]],rt[dep[x]],1,n,pos[get_lca(x,nex)],-1);
			if (nex&&pre) modify(rt[dep[x]],rt[dep[x]],1,n,pos[get_lca(nex,pre)],1);
		}
		rep(i,1,n) if (!rt[i]) rt[i]=rt[i-1];
		for (int x,d,w,lastans=0;m--;) {
			x=read(),d=read();
			// x=read()^lastans,d=read()^lastans;
			w=std:: min(dep[x]+d,n);
			lastans=query(rt[w],1,n,pos[x],pos[x]+size[x]-1);
			printf("%d\n", lastans);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值