【bzoj 4771】 七彩树

来源:互联网 发布:cms是什么品牌摄像头 编辑:程序博客网 时间:2024/04/28 15:54

Description

给定一棵树,有n个点,点有颜色,颜色范围[1,n]。
现有m个询问,形如x d
表示询问点x子树内与x深度差不超过d的点集中,不同的颜色种类数
强制在线
要求做到nlogn
1<=n<=100000 1<=m<=100000

离线

离线的话方法很多,其中一种是线段树合并。
开一棵线段树,下标是颜色,若该颜色出现过,那么值是最浅深度
再开一棵线段树,下表是深度,值是子树内深度为x的不同颜色数
我们用颜色线段树来辅助深度线段树,后者用来计算各个点处的答案。颜色线段树合并的时候,如果发现颜色重复,就在深度线段树中的max(depmin[x],depmin[y])处减重

在线

将上面的线段树合并过程中的线段树可持久化即可支持在线询问
至于空间复杂度,其实还是O(nlogn)的,因为当线段树合并的时候,只有两棵线段树共有的结点需要新建,而合并过程的共有结点总数就是O(nlogn),那么最后可持久化新建节点的总数也是O(nlogn)

dfs序

这是基于dfs序的另一种做法,考虑到是子树问题一个显然的思路是往dfs序方向上去想
先不考虑深度限制,可以用主席树来做(相当于二维,一维dfs序区间一维颜色)
现在有了深度限制,题目显然不允许再多套一维了(深度)
考虑如何消掉一维
当没有深度限制的时候,将同种颜色的点拉出来按照dfn排序。每个点对应位置+1,两两相邻lca处-1。这样显然是对的,子树查询就是dfs序上区间求和,只需要一维的线段树即可
那么再用函数式数据结构多套上一维深度就能支持询问了
复杂度也是O(nlogn)

Code

#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;++i)#define fd(i,b,a) for(int i=b;i>=a;--i)#define efo(i,v,u) for(int i=BB[v],u=B[BB[v]][1];i;i=B[i][0],u=B[i][1])#define max(x,y) ((x)>(y)?(x):(y))#define min(x,y) ((x)<(y)?(x):(y))#define mset(a,x) memset(a,x,sizeof(a))using namespace std;typedef long long ll;char ch;void read(int &n){n=0;int p=1;for(ch=getchar();ch<'0' || ch>'9';ch=getchar())if(ch=='-') p=-1;for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';n*=p;}const int N=1e5+5;int n,c[N],fa[N],B0,BB[N],B[N][2],dep[N];void link(int u,int v){B[++B0][1]=v,B[B0][0]=BB[u],BB[u]=B0;}int p1,rt1[N],ls1[N*20],rs1[N*20],dmn[N*20];//color depint p2,rtdel,rt2[N],ls2[N*80],rs2[N*80],sm[N*80];//dep ansint ins1(int l,int r,int x,int d){    int v=++p1;    if(l==r){dmn[v]=d;return v;}    int mid=l+r>>1;    if(x<=mid) ls1[v]=ins1(l,mid,x,d);    else rs1[v]=ins1(mid+1,r,x,d);    return v;}int ins2(int l,int r,int x,int y){    int v=++p2;    sm[v]=y;    if(l==r) return v;    int mid=l+r>>1;    if(x<=mid) ls2[v]=ins2(l,mid,x,y);    else rs2[v]=ins2(mid+1,r,x,y);    return v;}int merge2(int x,int y){    if(!x || !y) return x+y;    int v=++p2;    if(!ls2[x] && !rs2[x])//leaf    {        sm[v]=sm[x]+sm[y];        return v;    }    ls2[v]=merge2(ls2[x],ls2[y]);    rs2[v]=merge2(rs2[x],rs2[y]);    sm[v]=sm[ls2[v]]+sm[rs2[v]];    return v;}int merge1(int x,int y){    if(!x || !y) return x+y;    if(!ls1[x] && !rs1[x])//leaf    {        rtdel=merge2(rtdel,ins2(1,n,max(dmn[x],dmn[y]),-1));        dmn[x]=min(dmn[x],dmn[y]);        return x;    }    ls1[x]=merge1(ls1[x],ls1[y]);    rs1[x]=merge1(rs1[x],rs1[y]);    return x;}void dfs(int v){    rt1[v]=ins1(1,n,c[v],dep[v]);    rt2[v]=ins2(1,n,dep[v],1);    efo(i,v,u)    {        dep[u]=dep[v]+1;        dfs(u);    }    rtdel=0;    efo(i,v,u)    {        rt1[v]=merge1(rt1[v],rt1[u]);        rt2[v]=merge2(rt2[v],rt2[u]);    }    rt2[v]=merge2(rt2[v],rtdel);}int query(int v,int l,int r,int x,int y){    if(l==x && r==y) return sm[v];    int mid=l+r>>1;    if(y<=mid) return query(ls2[v],l,mid,x,y);    else    if(x>mid) return query(rs2[v],mid+1,r,x,y);    else    return query(ls2[v],l,mid,x,mid)+query(rs2[v],mid+1,r,mid+1,y);}int main(){    freopen("bzoj4771.in","r",stdin);    freopen("bzoj4771.out","w",stdout);    int cas,x,y,Q,d;    for(read(cas);cas;cas--)    {        mset(dmn,127);mset(rt1,0);mset(rt2,0);        B0=0;mset(BB,0);        fo(i,1,p1) ls1[i]=rs1[i]=0;p1=0;        fo(i,1,p2) ls2[i]=rs2[i]=sm[i]=0;p2=0;        read(n),read(Q);        fo(i,1,n) read(c[i]);        fo(i,2,n) read(fa[i]),link(fa[i],i);        dep[1]=1;dfs(1);        int ans=0;        while(Q--)        {            read(x),read(d);x^=ans,d^=ans;            printf("%d\n",ans=query(rt2[x],1,n,dep[x],min(n,dep[x]+d)));        }    }    return 0;}