线段树合并练习3——HDU 5709 Claris Loves Painting

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Love_mona/article/details/81290422

蒟蒻的垂死挣扎

我跟你讲,这题巨火无比。
这题求子树中深度不超过dep[u]+D的颜色种类数,甚至还十分过分的要求强制在线,然后我脑袋里浮现出了树套树套树.....就很绝望。但是仔细想想,运用之前主席树的套路,我们可以记录某种颜色深度最浅的位置,在这个位置上贡献答案。那么我们考虑记录一个Min数组表示每种颜色出现的最浅深度,每个点建一棵下标为深度的线段树记录颜色种数即可。然后我们还会发现,这个Min数组也是要对于每个点开的,所以也开线段树,都要合并。接下来考虑怎么减去重复的贡献,我们在合并Min的那棵线段树时,可以找到两边都有的点,那么直接在里面Modify减去贡献即可。

之前我问萝卜这个是不是两只log的,他说是,但是我总觉得不对,自己想想发现是一只。

时间复杂度的证明:

线段树合并的复杂度不用证明吧。。。总共 O(nlogn)

而在合并的时候,看似里面套了一个Modify两只log,实际上来说只有颜色相同才会Modify,意思就是说总共不会进行超过颜色种数次更新,所以总共的复杂度也是一只log

询问就是普通的线段树查询,一个log

总复杂度 O((n+m)logn) 不过基于两颗线段树的操作,常数巨大。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<vector>
#define ll long long
#define MOD 1000000007
#define N 110000
#define RG register

using namespace std;

inline int read(){
    RG int x=0,o=1; RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if(ch=='-') o=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=((x<<3)+(x<<1))+ch-'0',ch=getchar();
    return x*o;
}

int n,m,top,first[N],dep[N],col[N];
struct mona { int nxt,en; } s[N<<1];
inline void Insert(int x,int y) { s[++top]=(mona) { first[x],y },first[x]=top; }
struct SegTree {
	int tot,ls[N*100],rs[N*100],w[N*100],root[N*100];
	inline void Build(int l,int r,int &x,int pos,int val){
		x=++tot,w[x]=val,ls[x]=rs[x]=0;
		if(l==r) return ; int mid=(l+r)>>1;
		if(pos<=mid) Build(l,mid,ls[x],pos,val);
		else Build(mid+1,r,rs[x],pos,val);
	}
	inline void Modify(int l,int r,int &x,int y,int pos){
		x=++tot,ls[x]=ls[y],rs[x]=rs[y],w[x]=w[y]-1; if(l==r) return ;
		int mid=(l+r)>>1;
		if(pos<=mid) Modify(l,mid,ls[x],ls[y],pos);
		else Modify(mid+1,r,rs[x],rs[y],pos);
	}
	inline int Query(int l,int r,int now,int L,int R){
		if(L>R) return 0; if(l>=L&&r<=R) return w[now];
		int mid=(l+r)>>1,ans=0;
		if(L<=mid) ans+=Query(l,mid,ls[now],L,R);
		if(R>mid) ans+=Query(mid+1,r,rs[now],L,R);
		return ans;
	}
	inline int Merge_D(int u,int v){
		if(!u||!v) return u+v;
		int t=++tot; w[t]=w[u]+w[v];
		ls[t]=Merge_D(ls[u],ls[v]);
		rs[t]=Merge_D(rs[u],rs[v]);
		return t;
	}
	inline void clear() { for(RG int i=1;i<=tot;++i) ls[i]=rs[i]=root[i]=w[i]=0; }
} M,D;
inline int Merge_M(int u,int v,int l,int r,int rt){
	if(!u||!v) return u+v; int t=++M.tot,mid=(l+r)>>1;
	if(l==r&&M.w[u]&&M.w[v]){
		int sum=min(M.w[u],M.w[v]); M.w[t]=sum;
		D.Modify(1,n,D.root[rt],D.root[rt],M.w[u]+M.w[v]-sum);
	}
	M.ls[t]=Merge_M(M.ls[u],M.ls[v],l,mid,rt);
	M.rs[t]=Merge_M(M.rs[u],M.rs[v],mid+1,r,rt);
	return t;
}
inline void Dfs(int k,int Dep){
	dep[k]=Dep,M.Build(1,n,M.root[k],col[k],Dep);
	D.Build(1,n,D.root[k],Dep,1);
	for(RG int i=first[k];i;i=s[i].nxt){
		int en=s[i].en; Dfs(en,Dep+1);
		D.root[k]=D.Merge_D(D.root[k],D.root[en]);
		M.root[k]=Merge_M(M.root[k],M.root[en],1,n,k);
	}
}

int main(){
	RG int T=read();
	while(T--){
		D.tot=M.tot=top=0; RG int x,lst_ans=0;
		for(RG int i=1;i<=n;++i) first[i]=0;
		n=read(),m=read();
		for(RG int i=1;i<=n;++i) col[i]=read();
		for(RG int i=2;i<=n;++i) x=read(),Insert(x,i);
		Dfs(1,1);
		for(RG int i=1;i<=m;++i){ 
			int x=read()^lst_ans,d=read()^lst_ans;
			printf("%d\n",lst_ans=D.Query(1,n,D.root[x],dep[x],dep[x]+d));
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Love_mona/article/details/81290422