【HDU5709】Claris Loves Painting

题目

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5709
给定一棵树,每个节点有颜色,每次询问给出\(x,d\),要求输出在\(x\)的字数内,所有距离\(x\)不超过\(d\)的点的颜色总数。定义两点之间的距离为边数\(+1\)

思路

调了\(6h\)才调处来。。。心态爆炸。
对于每一个节点开两棵线段树。

  • \(x\)节点的第一棵线段树的区间\([l,r]\)表示在\(x\)的子树中,深度在\([l,r]\)之内的点有多少种第一次出现的颜色,也就是说,如果深度在\([dep[x],l-1]\)有颜色\(p\),深度在\([l,r]\)中也有颜色\(p\),那么颜色\(p\)\([l,r]\)是不做贡献的,因为在\(x\)的字数内深度更浅处已经出现了颜色\(p\)
  • \(x\)节点的第二棵线段树只有叶子结点有用,叶子结点\([i,i]\)表示颜色\(i\)\(x\)的子树内的最浅深度。

那么显然,对于一个询问\(x,d\),其答案就是在\(x\)的第一棵线段树中查询区间\([dep[x],dep[x]+d]\)的和,因为我们保证了每种颜色只有深度最浅才有贡献,所以不用担心拆分成两个子区间时两个子区间的贡献有重复。
假设我们已经处理好了\(x\)的子树,那么现在需要维护\(x\)的两棵线段树。
首先对于第一棵线段树,我们先直接合并。这样会造成贡献重复。比如说\(x\)的两个子节点\(y_1,y_2\),它们的子树中都含有颜色\(p\),那么直接合并时,颜色\(p\)就会对\(x\)做两次贡献。
那么我们只需要将\(y_1,y_2\)颜色\(p\)节点中深度较深的对\(x\)的贡献删除即可。那么我们在合并第二棵线段树时,如果两棵线段树含有相通的叶子结点\([i,i]\)(也就意味着都有颜色\(i\)),那么就将两棵线段树的颜色\(i\)的深度取\(max\)值在\(x\)的第一棵线段树中删除,取\(min\)值作为\(x\)的颜色\(i\)的最小深度。
那么\(x\)的两棵线段树我们都维护好了。\(dfs\)一遍即可求解。
为什么我觉得时间复杂度是\(O(Tm\log^2 n)\)啊。。。网上好像都说是\(O(Tm\log n)\)的。。。

注意事项(调了6h犯的傻逼错误)

  1. 初始化时千万不要\(memset\)清空线段树,会T飞。
  2. 查询线段树1的区间和时要判断这个节点是否存在,如果不存在要返回\(0\),否则会死循环。
  3. 线段树空间请开到\(10^7\)以上。当然可能\(10^7\)以下是可以过的。
  4. 每次合并、修改时要新建节点,否则原来节点的信息无法保存。
  5. 如果您测样例时发现输出了前几个,然后就死循环了,且输出的上一个答案是错的,那么请先找到出错的位置,因为死循环可能是出错后,\(lastans⊕x\)\(0\)或大于\(n\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=500010,M=30000010;
int n,m,T,totel,last,col[N],head[N],rt1[N],rt2[N],dep[N];

struct edge
{
	int next,to;
}e[N];

void add(int from,int to)
{
	e[++totel].to=to;
	e[totel].next=head[from];
	head[from]=totel;
}

struct Treenode1
{
	int lc,rc,cnt;
};

struct Treenode2
{
	int lc,rc,mindep;
};

struct Tree1
{
	Treenode1 tree[M];
	int tot;
	
	void update(int &x,int l,int r,int k,int val)
	{
		tree[++tot]=tree[x]; x=tot;
		tree[x].cnt+=val;
		if (l==k && r==k) return;
		int mid=(l+r)>>1;
		if (k<=mid) update(tree[x].lc,l,mid,k,val);
			else update(tree[x].rc,mid+1,r,k,val);
	}
	
	int merge(int x,int y)
	{
		if (!x || !y) return x+y;
		int p=++tot; tree[p]=tree[x];
		tree[p].cnt=tree[x].cnt+tree[y].cnt;
		tree[p].lc=merge(tree[x].lc,tree[y].lc);
		tree[p].rc=merge(tree[x].rc,tree[y].rc);
		return p;
	}
	
	int ask(int x,int l,int r,int ql,int qr)
	{
		if (!x) return 0;
		if (l==ql && r==qr) return tree[x].cnt;
		int mid=(l+r)>>1;
		if (qr<=mid) return ask(tree[x].lc,l,mid,ql,qr);
		if (ql>mid) return ask(tree[x].rc,mid+1,r,ql,qr);
		return ask(tree[x].lc,l,mid,ql,mid)+ask(tree[x].rc,mid+1,r,mid+1,qr);
	}
}tree1;

struct Tree2
{
	Treenode2 tree[M];
	int tot;
	
	void update(int &x,int l,int r,int k,int val)
	{
		tree[++tot]=tree[x]; x=tot;
		if (l==k && r==k)
		{
			tree[x].mindep=val;
			return;
		}
		int mid=(l+r)>>1;
		if (k<=mid) update(tree[x].lc,l,mid,k,val);
			else update(tree[x].rc,mid+1,r,k,val);
	}
	
	int merge(int x,int y,int l,int r,int root) 
	{
		if (!x || !y) return x+y;
		int p=++tot; tree[p]=tree[x];
		if (l==r)
		{
			tree[p].mindep=min(tree[x].mindep,tree[y].mindep);
			tree1.update(rt1[root],1,n,max(tree[x].mindep,tree[y].mindep),-1);
		}
		else
		{
			int mid=(l+r)>>1;
			tree[p].lc=merge(tree[x].lc,tree[y].lc,l,mid,root);
			tree[p].rc=merge(tree[x].rc,tree[y].rc,mid+1,r,root);
		}
		return p;
	}
}tree2;

void dfs(int x)
{
	tree1.update(rt1[x],1,n,dep[x],1);
	tree2.update(rt2[x],1,n,col[x],dep[x]);
	for (register int i=head[x];~i;i=e[i].next)
	{
		dep[e[i].to]=dep[x]+1;
		dfs(e[i].to);
		rt1[x]=tree1.merge(rt1[x],rt1[e[i].to]);
		rt2[x]=tree2.merge(rt2[x],rt2[e[i].to],1,n,x);
	}
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&m);
		for (register int i=1;i<=n;i++)
			rt1[i]=rt2[i]=0,head[i]=-1;
		totel=last=tree1.tot=tree2.tot=0; dep[1]=1;
		
		for (register int i=1;i<=n;i++) 
			scanf("%d",&col[i]);
		for (register int i=2,x;i<=n;i++)
		{
			scanf("%d",&x);
			add(x,i);
		}
		dfs(1);
		while (m--)
		{
			int x,d;
			scanf("%d%d",&x,&d); 
			x^=last; d^=last;
			last=tree1.ask(rt1[x],1,n,dep[x],min(dep[x]+d,n));
			printf("%d\n",last);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/12245301.html