• BZOJ4771 七彩树


    题意

    给定一棵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的所有点中出现了多少种本质不同的颜色。请写一个程序,快速回答这些询问。

    正整数(T(1<=T<=500)),表示测试数据的组数。
    正整数(n(1<=n<=100000))(m(1<=m<=100000)),表示节点数和询问数。

    分析

    参照LowestJN的题解。

    先考虑没有深度限制,要计算子树中不同的颜色的个数,就令所有节点的初始权值为1,就把两两相同颜色的节点的LCA的权值-1,然后求一遍子树权值和就可以了。
    但是这样做复杂度是(O(n^2)),而且会在某个节点多次-1。其实只要把颜色相同的节点提出来,按照dfs序的标号排个序,然后在相邻的节点的LCA处-1就行了。

    然后考虑有深度限制,仔细想想发现这个限制特别像主席树,那么就以深度排序,以深度为时间点建主席树,那么上面说的对节点修改的操作也要在这个时候做,可以用set维护同一颜色以dfn为序的前驱后继。

    时间复杂度(O(n log n + m log n))

    有趣的空间问题,理论(O(n log n)),实际上处理不同深度的时候同一节点最多会操作树链4次,(4 log_2 n = 66.438561897747246957406388589788),而我的程序要开到70n才能过。

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    template<class T>il T read()
    {
    	rg T data=0;
    	rg int w=1;
    	rg char ch=getchar();
    	while(!isdigit(ch))
    	{
    		if(ch=='-')
    			w=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    	{
    		data=data*10+ch-'0';
    		ch=getchar();
    	}
    	return data*w;
    }
    template<class T>il T read(rg T&x)
    {
    	return x=read<T>();
    }
    typedef long long ll;
    
    co int N=1e5+1;
    int c[N];
    int nx[N],to[N],fa[N],dep[N],siz[N],son[N];
    
    void dfs1(int x)
    {
    	dep[x]=dep[fa[x]]+1,siz[x]=1,son[x]=0;
    	for(int i=to[x];i;i=nx[i])
    	{
    		dfs1(i);
    		siz[x]+=siz[i];
    		if(siz[i]>siz[son[x]])
    			son[x]=i;
    	}
    }
    
    int dfn,pos[N],ref[N],top[N];
    
    void dfs2(int x,int top)
    {
    	pos[x]=++dfn,ref[dfn]=x,::top[x]=top;
    	if(!son[x])
    		return;
    	dfs2(son[x],top);
    	for(int i=to[x];i;i=nx[i])
    	{
    		if(i==son[x])
    			continue;
    		dfs2(i,i);
    	}
    }
    
    int lca(int x,int y)
    {
    	while(top[x]!=top[y])
    	{
    		if(dep[top[x]]<dep[top[y]])
    			std::swap(x,y);
    		x=fa[top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    
    int id[N];
    
    bool cmp(int u,int v)
    {
    	return dep[u]<dep[v];
    }
    
    std::set<int>S[N];
    std::set<int>::iterator it;
    
    co int LG=70;
    int root[N],tot;
    namespace T
    {
    	int L[N*LG],R[N*LG],sum[N*LG];
    	
    	int clone(int y)
    	{
    		int x=++tot;
    		L[x]=L[y],R[x]=R[y],sum[x]=sum[y];
    		return x;
    	}
    	
    	void insert(int&x,int l,int r,int p,int v)
    	{
    		x=clone(x);
    		sum[x]+=v;
    		if(l==r)
    			return;
    		int m=(l+r)/2;
    		if(p<=m)
    			insert(L[x],l,m,p,v);
    		else
    			insert(R[x],m+1,r,p,v);
    	}
    	
    	int query(int x,int l,int r,int ql,int qr)
    	{
    		if(ql<=l&&r<=qr)
    			return sum[x];
    		int m=(l+r)/2;
    		if(qr<=m)
    			return query(L[x],l,m,ql,qr);
    		if(ql>=m+1)
    			return query(R[x],m+1,r,ql,qr);
    		return query(L[x],l,m,ql,qr)+query(R[x],m+1,r,ql,qr);
    	}
    }
    
    int main()
    {
    //	freopen("BZOJ4771.in","r",stdin);
    //	freopen("BZOJ4771.out","w",stdout);
    	int t;
    	read(t);
    	while(t--)
    	{
    		int n,m;
    		read(n),read(m);
    		for(int i=1;i<=n;++i)
    		{
    			read(c[i]);
    			to[i]=nx[i]=0;
    			id[i]=i;
    			S[i].clear();
    		}
    		for(int i=2;i<=n;++i)
    		{
    			read(fa[i]);
    			nx[i]=to[fa[i]],to[fa[i]]=i;
    		}
    		dfs1(1);
    		dfn=0;
    		dfs2(1,1);
    		std::sort(id+1,id+n+1,cmp);
    		root[0]=0,tot=0;
    		int p=1;
    		for(int i=1;i<=n;++i)
    		{
    			root[i]=root[i-1];
    			while(p<=n&&dep[id[p]]<=i)
    			{
    				int x=0,y=0,pcol=c[id[p]];
    				it=S[pcol].lower_bound(pos[id[p]]);
    				if(it!=S[pcol].end())
    					y=ref[*it];
    				if(it!=S[pcol].begin())
    					x=ref[*--it];
    				T::insert(root[i],1,n,pos[id[p]],1);
    				if(x)
    					T::insert(root[i],1,n,pos[lca(x,id[p])],-1);
    				if(y)
    					T::insert(root[i],1,n,pos[lca(id[p],y)],-1);
    				if(x&&y)
    					T::insert(root[i],1,n,pos[lca(x,y)],1);
    				S[pcol].insert(pos[id[p]]);
    				++p;
    			}
    		}
    		int ans=0;
    		while(m--)
    		{
    			int x=read<int>()^ans,d=read<int>()^ans;
    			d=std::min(dep[x]+d,n);
    			printf("%d
    ",ans=T::query(root[d],1,n,pos[x],pos[x]+siz[x]-1));
    		}
    	}
    	return 0;
    }
    

    第一の問題

    问题描述

    给定一个序列,每个元素有两种属性,一个是颜色,一个是权值。

    每次询问一个区间内权值小于某值的元素中不同颜色的个数。

    规定询问的区间不相交(可能包含)。

    输入格式

    第一行一个正整数n,表示序列长度。

    接下来n行,每行两个整数,表示颜色和权值。

    下一行一个整数m,表示询问次数。

    最后m行,每行两个整数l,r,v,表示询问[l,r]区间内权值小于v的元素中不同颜色的个数。

    强制在线的方式:对于输入的l,r,v,异或上lastans。规定第一次询问操作的lastans=0。

    输出格式

    输出m行每行一个整数,分别对应答案。

    样例输入

    样例输出

    数据范围

    对于100%的数据,(1 leq n,m leq 10^5)

  • 相关阅读:
    vc++ generate random via random_device and uniform_int_distribution
    Windows OpenGL 图像对比度调节
    OpenGL ES 版本介绍
    Windows OpenGL 图像伽马线
    Windows OpenGL 图像反色
    OpenGL ES 简介
    Windows OpenGL ES 图像饱和度调节
    修改 Docker 数据根目录
    mysql分库备份与分表备份
    docker dockerfile 映射端口范围 批量映射端口
  • 原文地址:https://www.cnblogs.com/autoint/p/10318783.html
Copyright © 2020-2023  润新知