[HDU5709]Claris Loves Painting

vjudge

description

给定一棵\(n\)点的树,每个节点上有一个颜色,每次询问一个点的子树中与这个点距离不超过\(d\)的点的颜色有多少种。强制在线。

sol

对每个点开两棵线段树,一棵以颜色为下标,表示每种颜色出现的最浅深度,一棵以深度为下标,表示以该深度为最浅深度的颜色有多少种。
这两棵线段树都可以直接由儿子合并上来。
在合并前一棵线段树的时候可能会导致第二棵线段树的修改(两个节点颜色相同,那就要删除深度更大的点的贡献),处理一下即可。
复杂度\(O(n\log n)\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e5+5;
struct node{int ls,rs,w;}t[N*100];
int n,m,col[N],nxt[N],head[N],rt1[N],rt2[N],tot,dep[N],ans;
void build(int &x,int l,int r,int p,int v){
    x=++tot;if (l==r) {t[x].w=v;return;}int mid=l+r>>1;
    if (p<=mid) build(t[x].ls,l,mid,p,v);
    else build(t[x].rs,mid+1,r,p,v);
}
void modify(int &x,int l,int r,int p,int v){
    t[++tot]=t[x];t[x=tot].w+=v;if (l==r) return;int mid=l+r>>1;
    if (p<=mid) modify(t[x].ls,l,mid,p,v);
    else modify(t[x].rs,mid+1,r,p,v);
}
int merge_1(int x,int y,int l,int r,int u){
    if (!x||!y) return x|y;int z=++tot,mid=l+r>>1;
    if (l==r){
        t[z].w=min(t[x].w,t[y].w);
        modify(rt2[u],1,n,max(t[x].w,t[y].w),-1);
    }
    t[z].ls=merge_1(t[x].ls,t[y].ls,l,mid,u);
    t[z].rs=merge_1(t[x].rs,t[y].rs,mid+1,r,u);
    return z;
}
int merge_2(int x,int y){
    if (!x||!y) return x|y;int z=++tot;t[z].w=t[x].w+t[y].w;
    t[z].ls=merge_2(t[x].ls,t[y].ls);
    t[z].rs=merge_2(t[x].rs,t[y].rs);
    return z;
}
int query(int x,int l,int r,int ql,int qr){
    if (l>=ql&&r<=qr) return t[x].w;int mid=l+r>>1,s=0;
    if (ql<=mid) s+=query(t[x].ls,l,mid,ql,qr);
    if (qr>mid) s+=query(t[x].rs,mid+1,r,ql,qr);
    return s;
}
void dfs(int u){
    build(rt1[u],1,n,col[u],dep[u]);modify(rt2[u],1,n,dep[u],1);
    for (int v=head[u];v;v=nxt[v]){
        dep[v]=dep[u]+1,dfs(v);
        rt1[u]=merge_1(rt1[u],rt1[v],1,n,u);
        rt2[u]=merge_2(rt2[u],rt2[v]);
    }
}
int main(){
    int Case=gi();while (Case--){
        n=gi();m=gi();ans=0;
        for (int i=1;i<=tot;++i) t[i]=(node){0,0,0};tot=0;
        for (int i=1;i<=n;++i) col[i]=gi(),head[i]=rt1[i]=rt2[i]=0;
        for (int i=2,f;i<=n;++i) nxt[i]=head[f=gi()],head[f]=i;
        dep[1]=1;dfs(1);
        while (m--){
            int x=gi()^ans,d=gi()^ans;
            printf("%d\n",ans=query(rt2[x],1,n,dep[x],dep[x]+d));
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/9398700.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值