七彩树 HYSBZ - 4771线段树合并

时间:2019-01-18
本文章向大家介绍七彩树 HYSBZ - 4771线段树合并,主要包括七彩树 HYSBZ - 4771线段树合并使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Part1

本题是查找一个节点x的字树内深度与其相差不超过d的节点有多少种颜色。如果单纯考虑有多少个点,应该如何求,如何维护深度在一个区间内且是x的后代的信息呢?我们可以每个节点用一颗以下标为权值的线段树来维护(后面记为线段树X)。用线段树合并来完成所有信息的处理。但如何查找深度在一个区间里的信息呢?我们可以将每个节点重新以深度编号,编号与深度正相关(也就是编号越大的点深度越大),在预处理处每个深度最大的编号。这样就可以区间求和来解决这一简化后的问题。

Part2

对于“出现了多少种本质不同的颜色”这一句话,不难联想到Turing Tree这道题。只不过变为了在子树内查询而不是在序列上,但大体还是类似的。

相同颜色的节点,我们只用深度最浅(也就是离根最近)的节点。故我们可以对每个节点再开一个线段树维护这个节点的子树内每种颜色的节点的最浅位置(后面记为线段树Y),每次线段树合并时去掉深度大的颜色相同的点在当前节点的线段树X里的贡献。每个节点的线段树X只存每种颜色所在深度最浅的位置(重新编号后的)。

Part3

题目里的小细节注意一下。(1<=T<=500),如果对每一组测试数据都清空数组会TLE,由于n和m的总和不超过500000,故每次只用清空上次使用过的数组即可。

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define M 100005
#define INF 1061109567
using namespace std;
bool cur1;
void check_max(int &x,int y){if(x<y)x=y;}
void check_min(int &x,int y){if(x>y)x=y;}
struct E{
    int to,nx;
}edge[M<<1];
int tot,head[M];
void Addedge(int a,int b){
    edge[++tot].to=b;
    edge[tot].nx=head[a];
    head[a]=tot;
}
int C[M],fa[M],dep[M],n,id;
int ID[M];//按照深度排序 
int ID_dep[M];//每个排序后的节点的深度 
struct SegT2{
    int tot;
    int Root[M],Lson[M*50],Rson[M*50];
    int sum[M*50];//每个子树内的信息  和Turing Tree没什么区别 
    void Create(int &p){
    	int tid=++tot;
		Lson[tid]=Lson[p];
    	Rson[tid]=Rson[p];
    	sum[tid]=sum[p];
    	p=tid;
	}
    SegT2(){tot=0;}
	void clear(){
		for(int i=1;i<=n;i++)Root[i]=0;
		for(int i=0;i<=tot;i++)Lson[i]=Rson[i]=sum[i]=0;
        tot=0;
    }
    void Updata(int L,int R,int x,int op,int &tid){
    	Create(tid);
        sum[tid]+=op;
        if(L==R)return;
        int mid=(L+R)>>1;
        if(x<=mid)Updata(L,mid,x,op,Lson[tid]);
        else Updata(mid+1,R,x,op,Rson[tid]);
    }
    int Query(int L,int R,int Lx,int Rx,int tid){
        if(Lx<=L&&R<=Rx)return sum[tid];
        int mid=(L+R)>>1;
        if(Rx<=mid)return Query(L,mid,Lx,Rx,Lson[tid]);
        else if(Lx>mid)return Query(mid+1,R,Lx,Rx,Rson[tid]);
        else return Query(L,mid,Lx,mid,Lson[tid])+Query(mid+1,R,mid+1,Rx,Rson[tid]);
    }
    int Merge(int x,int y,int L,int R){
        if(!x||!y)return x+y;
        int tid=++tot;
        int mid=(L+R)>>1;
        sum[tid]=sum[x]+sum[y];
        Lson[tid]=Merge(Lson[x],Lson[y],L,mid);
        Rson[tid]=Merge(Rson[x],Rson[y],mid+1,R);
        return tid;
    }
    void Text(int tid,int L,int R){
        int mid=(L+R)>>1;
        if(L==R)return;
        Text(Lson[tid],L,mid);
        Text(Rson[tid],mid+1,R);
    }
}ST2;
struct SegT1{//颜色COLOR 
    int tot; 
    int Root[M],Lson[M*50],Rson[M*50];
    int num[M*50];//每个颜色所对应的最浅位置 
    int Pc;
    void Create(int &p){
    	int tid=++tot;
    	Lson[tid]=Lson[p];
    	Rson[tid]=Rson[p];
    	num[tid]=num[p];
    	p=tid;
	}
    SegT1(){tot=0;}
	void clear(){
		for(int i=0;i<=n;i++)Root[i]=0;
		for(int i=0;i<=tot;i++)Rson[i]=0,Lson[i]=0,num[i]=INF;//memset会T,手动清空 
        tot=0;
    }
    void Updata(int L,int R,int x,int d,int &tid){
        Create(tid);
        if(L==R){
            if(num[tid]>n||ID_dep[num[tid]]>ID_dep[d]){ 
                if(num[tid]<=n)ST2.Updata(1,n,num[tid],-1,ST2.Root[Pc]);//更新操作 
                ST2.Updata(1,n,d,1,ST2.Root[Pc]);
                num[tid]=d;
            }
            return;
        }
        int mid=(L+R)>>1;
        if(x<=mid)Updata(L,mid,x,d,Lson[tid]);
        else Updata(mid+1,R,x,d,Rson[tid]);
    }
    int Query(int L,int R,int x,int &tid){
        if(L==R)return num[tid];
        int mid=(L+R)>>1;
        if(x<=mid)return Query(L,mid,x,Lson[tid]);
        else return Query(mid+1,R,x,Rson[tid]);
    }
    int Merge(int x,int y,int L,int R){
        if(!x||!y)return (x+y);
        int tid=++tot;
        if(L==R){
            if(num[x]>n&&num[y]>n){
        		num[tid]=num[x]; 
				return tid;
			}
            int res=max(num[x],num[y]);
            if(res<=n)ST2.Updata(1,n,res,-1,ST2.Root[Pc]);//删去深度较大的 
            num[tid]=min(num[x],num[y]);
            return tid;
        }
        int mid=(L+R)>>1;
        Lson[tid]=Merge(Lson[x],Lson[y],L,mid);
        Rson[tid]=Merge(Rson[x],Rson[y],mid+1,R);
        return tid;
    }
}ST1;
int dep_mx[M];
void dfs(int now){
    for(int i=head[now];i;i=edge[i].nx){//把深度求出来 
        int nxt=edge[i].to;
        if(nxt==fa[now])continue;
        dep[nxt]=dep[now]+1;
        dfs(nxt);
    }
}
void redfs(int now){
	ST1.Root[now]=ST2.Root[now]=0;
    for(int i=head[now];i;i=edge[i].nx){
        int nxt=edge[i].to;
        redfs(nxt);
        ST1.Pc=now;
        ST1.Root[now]=ST1.Merge(ST1.Root[nxt],ST1.Root[now],1,id);
        ST2.Root[now]=ST2.Merge(ST2.Root[nxt],ST2.Root[now],1,n);
    }
    ST1.Pc=now;
    ST1.Updata(1,id,C[now],ID[now],ST1.Root[now]);
}
bool cmp(int x,int y){return dep[x]<dep[y];}
void Init(){
    dep[1]=1;
    ST1.clear();
    ST2.clear();
    tot=0;
    for(int i=1;i<=n;i++)head[i]=0,dep_mx[i]=0;
}
int B[M],P[M];
bool cur2;
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int m;
        scanf("%d%d",&n,&m);
        Init();
        for(int i=1;i<=n;i++)scanf("%d",&C[i]),B[i]=C[i];
        sort(B+1,B+n+1);
        id=unique(B+1,B+n+1)-B-1;
        for(int i=1;i<=n;i++)C[i]=lower_bound(B+1,B+id+1,C[i])-B;//离散化 
        for(int i=2;i<=n;i++){
            scanf("%d",&fa[i]);
            Addedge(fa[i],i);
        }
    	for(int i=1;i<=n;i++)P[i]=i;
        dfs(1);
        sort(P+1,P+n+1,cmp);
        for(int i=1;i<=n;i++)ID[P[i]]=i;//重新标号 
        for(int i=1;i<=n;i++)ID_dep[ID[i]]=dep[i];//存每一个编号对应的深度 
        for(int i=1;i<=n;i++)check_max(dep_mx[dep[i]],ID[i]);//预处理每个深度最大的编号 
        redfs(1);
        int ans=0;
        while(m--){
            int x,d;
            scanf("%d%d",&x,&d);
            x^=ans,d^=ans;
            printf("%d\n",ans=ST2.Query(1,n,dep_mx[dep[x]-1]+1,dep_mx[dep[x]+d]==0?n:dep_mx[dep[x]+d],ST2.Root[x]));//由于怕深度会超过子树最深深度