BZOJ4771:七彩树 (LCA+Treap+可持久化线段树)

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4771


题目分析:一道很妙的题。

先简化问题:假设没有深度限制,问题就变成了一棵子树中本质不同的颜色个数。只要将DFS序搞出来,就变成了一段区间中不同的数的个数,这个直接用可持久化线段树就可以解决了。有深度限制怎么办呢?我们可不可以按深度顺序将这些点加入主席树呢?答案是不可以的。因为以深度为顺序来加的话,加入一个点有可能要更改多棵主席树的形态,这就变成了可持久化树套树。所以我们不能将原问题转化成序列型问题考虑,要用一种更加巧妙的方法。

假设所有节点的颜色都不相同,且没有深度限制,那么询问一个点的答案就是其子树大小。假设有2个,3个或4个相同颜色的点,询问时答案会有什么变化呢?

我们发现,假设有k个相同颜色的点,将其按时间戳排好序。先令所有点权值为1,然后在第i-1个点和第i个点的lca处权值-1(2<=i<=k)。查询某个点的答案,就是它的子树权值之和。

再考虑按深度插入所有点。假设要在某种颜色的第i个点和第i+1个点之间新插入一个相同颜色的点,就先将第i个点和第i+1个点的lca权值加回1,然后在新点和第i个点,第i+1个点的lca处权值-1,最后令新点的权值为1。

要查询某种颜色的点的前驱和后继,对每种颜色开一棵treap即可。用可持久化线段树存储插入不同深度的点之后的每个点的权值。至于LCA嘛,我试着写了写传说中跑得飞快的树剖lca,顺便复习了一下BFS做树剖。

(这题的代码写了我一个晚上,然后第二天又debug了一个中午,发现原因居然是多组数据令我队列的边界出了问题。我写对拍的时候一直固定用n=1000来对拍,n相同时那段代码就不会有错,导致我一直找不出错QAQ)


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=101000;
const int maxl=25;

const long long M1=998244553;
const long long M2=1000000007;
const long long M3=1333333331;
typedef long long LL;

struct Seg
{
    int sum;
    Seg *Lson,*Rson;
} tree[maxn*maxl<<2];
Seg *Seg_Root[maxn];
int Scur;

struct Tnode
{
    int id,fix;
    Tnode *lson,*rson;
} Treap[maxn];
Tnode *Root[maxn];
int Tcur;
LL seed;

struct edge
{
    int obj;
    edge *Next;
} e[maxn];
edge *head[maxn];
int cur;

int fa[maxn];
int dep[maxn];
int Size[maxn];
int Son[maxn];

int Top[maxn];
int dfsx[maxn];
int w[maxn];
int ed[maxn];

int que[maxn];
int tail;

int cnt[maxn];
int Rank[maxn];
int max_dep;

int col[maxn];
int t,n,m;

void Add(int x,int y)
{
    cur++;
    e[cur].obj=y;
    e[cur].Next=head[x];
    head[x]=e+cur;
}

void Bfs()
{
    tail=1;
    que[1]=1;
    fa[1]=dep[1]=1;
    for (int i=1; i<=n; i++)
    {
        int node=que[i];
        for (edge *p=head[node]; p; p=p->Next)
        {
            int son=p->obj;
            fa[son]=node;
            dep[son]=dep[node]+1;
            que[++tail]=son;
        }
    }

    Size[0]=0;
    for (int i=n; i>=2; i--)
    {
        int node=que[i];
        Size[ fa[node] ]+=Size[node];
        if ( Size[node]>Size[ Son[ fa[node] ] ] ) Son[ fa[node] ]=node;
    }

    Top[1]=dfsx[1]=w[1]=1;
    ed[1]=n;
    for (int i=1; i<=n; i++)
    {
        int node=que[i],son=Son[node],Time=w[node]+1;
        if (!son) continue;

        Top[son]=Top[node];
        dfsx[Time]=son;
        w[son]=Time;
        Time+=Size[son];
        ed[son]=Time-1;

        for (edge *p=head[node]; p; p=p->Next)
        {
            son=p->obj;
            if (son==Son[node]) continue;

            Top[son]=son;
            dfsx[Time]=son;
            w[son]=Time;
            Time+=Size[son];
            ed[son]=Time-1;
        }
    }
}

Seg *New_seg()
{
    Scur++;
    tree[Scur].sum=0;
    tree[Scur].Lson=tree[Scur].Rson=tree;
    return tree+Scur;
}

int Rand()
{
    seed=(seed*M1+M2)%M3;
    return (int)seed;
}

Tnode *New_node(int node)
{
    Tcur++;
    Treap[Tcur].id=node;
    Treap[Tcur].fix=Rand();
    Treap[Tcur].lson=Treap[Tcur].rson=NULL;
    return Treap+Tcur;
}

void Right_turn(Tnode *&P)
{
    Tnode *W=P->lson;
    P->lson=W->rson;
    W->rson=P;
    P=W;
}

void Left_turn(Tnode *&P)
{
    Tnode *W=P->rson;
    P->rson=W->lson;
    W->lson=P;
    P=W;
}

void Insert(Tnode *&P,int node)
{
    if (!P) P=New_node(node);
    else
        if ( w[node]<w[P->id] )
        {
            Insert(P->lson,node);
            if ( P->lson->fix<P->fix ) Right_turn(P);
        }
        else
        {
            Insert(P->rson,node);
            if ( P->rson->fix<P->fix ) Left_turn(P);
        }
}

int Find_prev(Tnode *P,int node,int ans)
{
    if (!P) return ans;
    if ( w[P->id]<w[node] ) return Find_prev(P->rson,node,P->id);
    return Find_prev(P->lson,node,ans);
}

int Find_succ(Tnode *P,int node,int ans)
{
    if (!P) return ans;
    if ( w[node]<w[P->id] ) return Find_succ(P->lson,node,P->id);
    return Find_succ(P->rson,node,ans);
}

int Lca(int u,int v)
{
    if (Top[u]==Top[v])
        if (w[u]<w[v]) return u;
        else return v;
    if (dep[ Top[u] ]<dep[ Top[v] ]) swap(u,v);
    return Lca(fa[ Top[u] ],v);
}

void Update(Seg *root,int L,int R,int x,int v)
{
    if (L==R)
    {
        root->sum+=v;
        return;
    }

    int mid=(L+R)>>1;
    Seg *y=New_seg();

    if (x<=mid)
    {
        (*y)=*(root->Lson);
        Update(y,L,mid,x,v);
        root->Lson=y;
    }
    else
    {
        (*y)=*(root->Rson);
        Update(y,mid+1,R,x,v);
        root->Rson=y;
    }

    root->sum=root->Lson->sum+root->Rson->sum;
}

void Work(Seg *&x,int node,int v)
{
    Seg *y=New_seg();
    (*y)=(*x);
    Update(y,1,n,w[node],v);
    x=y;
}

int Query(Seg *root,int L,int R,int x,int y)
{
    if ( y<L || R<x ) return 0;
    if ( x<=L && R<=y ) return root->sum;

    int mid=(L+R)>>1;
    int vl=Query(root->Lson,L,mid,x,y);
    int vr=Query(root->Rson,mid+1,R,x,y);

    return (vl+vr);
}

int main()
{
    freopen("4771.in","r",stdin);
    freopen("4771.out","w",stdout);

    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&n,&m);
        cur=-1;
        for (int i=1; i<=n; i++)
            scanf("%d",&col[i]),head[i]=NULL,Size[i]=1,Son[i]=0,Root[i]=NULL;
        for (int i=2; i<=n; i++)
        {
            int f;
            scanf("%d",&f);
            Add(f,i);
        }

        Bfs();

        max_dep=0;
        for (int i=1; i<=n; i++) max_dep=max(max_dep,dep[i]);
        for (int i=1; i<=max_dep; i++) cnt[i]=0;
        for (int i=1; i<=n; i++) cnt[ dep[i] ]++;
        for (int i=2; i<=max_dep; i++) cnt[i]+=cnt[i-1];
        for (int i=1; i<=n; i++) Rank[ cnt[ dep[i] ]-- ]=i;

        seed=fa[n];
        Scur=Tcur=-1;
        Seg_Root[0]=New_seg();
        tail=0;

        for (int i=1; i<=max_dep; i++)
        {
            Seg *x=Seg_Root[i-1];
            while ( dep[ Rank[tail+1] ]<=i && tail<n )
            {
                int node=Rank[++tail];
                Insert(Root[ col[node] ],node);
                int Prev=Find_prev(Root[ col[node] ],node,0);
                int Succ=Find_succ(Root[ col[node] ],node,0);

                int lca;
                if ( Prev && Succ ) lca=Lca(Prev,Succ),Work(x,lca,1);
                if (Prev) lca=Lca(Prev,node),Work(x,lca,-1);
                if (Succ) lca=Lca(node,Succ),Work(x,lca,-1);
                Work(x,node,1);
            }
            Seg_Root[i]=x;
        }

        int ans=0;
        for (int i=1; i<=m; i++)
        {
            int x,d;
            scanf("%d%d",&x,&d);
            x^=ans,d^=ans;
            d+=dep[x];
            d=min(d,max_dep);
            ans=Query(Seg_Root[d],1,n,w[x],ed[x]);
            printf("%d\n",ans);
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值