zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    Windows 08 R2_组策略
    Nginx常用配置实例(4)
    Nginx日常维护操作(3)
    Nginx配置文件(2)
    Nginx概述和安装(1)
    Zabbix实战-简易教程--通过公众平台企业号发送短信
    HDFS ZKFC自动切换原理分析
    HDFS ZKFC自动切换原理分析
    DataNode启动优化改进:磁盘检测并行化
    DataNode启动优化改进:磁盘检测并行化
  • 原文地址:https://www.cnblogs.com/stoorz/p/12245301.html
Copyright © 2011-2022 走看看