bzoj 4771: 七彩树

题意

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

强制在线

题解

求题解跑
我比较懒,用set解决了前驱后继的操作

CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
using namespace std;
const int N=500005;
int T;
int n,m;
int g[N];
struct qq
{
    int x,y,last;
}e[N];int num,last[N];
void init (int x,int y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
int L[N],R[N],cnt;
int fa[N][21];
int dep[N];
int mx;
int belong[N];
void dfs (int x)
{
    mx=max(mx,dep[x]);
    L[x]=++cnt;for (int u=1;u<=20;u++) fa[x][u]=fa[fa[x][u-1]][u-1];
    belong[cnt]=x;
    for (int u=last[x];u!=-1;u=e[u].last)
    {
        int y=e[u].y;
        dep[y]=dep[x]+1;
        fa[y][0]=x;dfs(y);
    }
    R[x]=cnt;
}
int c[N*25],s1[N*25],s2[N*25];
int rt[N];
queue<int> q;
set<int> s[N];
int lca (int x,int y)
{
    if (dep[x]>dep[y]) swap(x,y);
    for (int u=20;u>=0;u--)
        if (dep[fa[y][u]]>=dep[x])
            y=fa[y][u];
    if (x==y) return x;
    for (int u=20;u>=0;u--)
        if (fa[x][u]!=fa[y][u])
        {
            x=fa[x][u];y=fa[y][u];
        }
    return fa[x][0];
}
void change (int &now,int l,int r,int x,int xx) 
{
    if (now==0) {now=++num;c[now]=0;s1[now]=s2[now]=0;}
    c[now]+=xx;
    if (l==r) return ;
    int mid=(l+r)>>1;
    if (x<=mid) change(s1[now],l,mid,x,xx);
    else change(s2[now],mid+1,r,x,xx);
}
set<int>::iterator a,b;
void Merge (int rt1,int &rt2)
{
    if (rt1==0) return ;
    if (rt2==0) {rt2=rt1;return ;}
    c[rt2]+=c[rt1];
    Merge(s1[rt1],s1[rt2]);
    Merge(s2[rt1],s2[rt2]);
}
void solve ()
{
    for (int u=1;u<=n;u++) s[u].clear();
    num=0;q.push(1);
    while (!q.empty())
    {
        int x=q.front(),d=dep[x];q.pop();
    //  printf("%d %d\n",x,d);
        change(rt[d],1,n,L[x],1);
        //printf("1:%d %d\n",d,x);
        a=s[g[x]].lower_bound(L[x]);b=s[g[x]].upper_bound(L[x]);
        if (a!=s[g[x]].begin())
        {
            int LCA=lca(belong[*(--a)],x);a++;
        //  printf("-1:d:%d lca:%d x:%d y:%d\n",d,LCA,belong[*(--a)],x);a++;
            change(rt[d],1,n,L[LCA],-1);    
        }
        if (b!=s[g[x]].end())
        {
            int LCA=lca(belong[*b],x);
        //  printf("-2:d:%d lca:%d x:%d y:%d\n",d,LCA,belong[*b],x);
            change(rt[d],1,n,L[LCA],-1);
        }

        if (a!=s[g[x]].begin()&&b!=s[g[x]].end())
        {
            int LCA=lca(belong[*(--a)],belong[*b]);a++;
    //  printf("-3:d:%d lca:%d x:%d y:%d\n",d,LCA,belong[*(--a)],belong[*b]);a++;
            change(rt[d],1,n,L[LCA],1);
        }
        s[g[x]].insert(L[x]);
        for (int u=last[x];u!=-1;u=e[u].last)
        {
            int y=e[u].y;
            q.push(y);
        }
    }

    for (int u=2;u<=mx;u++) Merge(rt[u-1],rt[u]);   //printf("NO\n");
}
int get (int now,int l,int r,int L,int R)
{
    if (now==0) return 0;
//  printf("%d %d %d\n",l,r,c[now]);
    if (l==L&&r==R) return c[now];
    int mid=(l+r)>>1;
    if (R<=mid) return get(s1[now],l,mid,L,R);
    else if (L>mid) return get(s2[now],mid+1,r,L,R);
    else return get(s1[now],l,mid,L,mid)+get(s2[now],mid+1,r,mid+1,R);
}
int main()
{
    scanf("%d",&T);
    while (T--)
    {   
        memset(rt,0,sizeof(rt));
        num=0;memset(last,-1,sizeof(last));
        cnt=0;
        scanf("%d%d",&n,&m);
        for (int u=1;u<=n;u++) scanf("%d",&g[u]);
        for (int u=2;u<=n;u++)
        {
            int x;
            scanf("%d",&x);
            init(x,u);
        }
        mx=0;dep[1]=1;dfs(1);
        //printf("YES\n");
        /*for (int u=1;u<=n;u++) printf("%d ",L[u]);
        printf("\n");*/
        solve();
        int ans=0;
        for (int u=1;u<=m;u++)
        {
            int x,d;
            scanf("%d%d",&x,&d);
            x^=ans;d^=ans;
            ans=get(rt[min(mx,dep[x]+d)],1,n,L[x],R[x]);
            printf("%d\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值