首页 > 其他 > 详细

BZOJ4771 七彩树

时间:2019-01-25 12:06:15      阅读:195      评论:0      收藏:0      [点我收藏+]

题意

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

正整数\(T(1<=T<=500)\),表示测试数据的组数。
正整数\(n(1<=n<=100000)\)\(m(1<=m<=100000)\),表示节点数和询问数。

分析

参照LowestJN的题解。

先考虑没有深度限制,要计算子树中不同的颜色的个数,就令所有节点的初始权值为1,就把两两相同颜色的节点的LCA的权值-1,然后求一遍子树权值和就可以了。
但是这样做复杂度是\(O(n^2)\),而且会在某个节点多次-1。其实只要把颜色相同的节点提出来,按照dfs序的标号排个序,然后在相邻的节点的LCA处-1就行了。

然后考虑有深度限制,仔细想想发现这个限制特别像主席树,那么就以深度排序,以深度为时间点建主席树,那么上面说的对节点修改的操作也要在这个时候做,可以用set维护同一颜色以dfn为序的前驱后继。

时间复杂度\(O(n \log n + m \log n)\)

有趣的空间问题,理论\(O(n \log n)\),实际上处理不同深度的时候同一节点最多会操作树链4次,\(4 \log_2 n = 66.438561897747246957406388589788\),而我的程序要开到70n才能过。

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
    rg T data=0;
    rg int w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<class T>il T read(rg T&x)
{
    return x=read<T>();
}
typedef long long ll;

co int N=1e5+1;
int c[N];
int nx[N],to[N],fa[N],dep[N],siz[N],son[N];

void dfs1(int x)
{
    dep[x]=dep[fa[x]]+1,siz[x]=1,son[x]=0;
    for(int i=to[x];i;i=nx[i])
    {
        dfs1(i);
        siz[x]+=siz[i];
        if(siz[i]>siz[son[x]])
            son[x]=i;
    }
}

int dfn,pos[N],ref[N],top[N];

void dfs2(int x,int top)
{
    pos[x]=++dfn,ref[dfn]=x,::top[x]=top;
    if(!son[x])
        return;
    dfs2(son[x],top);
    for(int i=to[x];i;i=nx[i])
    {
        if(i==son[x])
            continue;
        dfs2(i,i);
    }
}

int lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
            std::swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}

int id[N];

bool cmp(int u,int v)
{
    return dep[u]<dep[v];
}

std::set<int>S[N];
std::set<int>::iterator it;

co int LG=70;
int root[N],tot;
namespace T
{
    int L[N*LG],R[N*LG],sum[N*LG];
    
    int clone(int y)
    {
        int x=++tot;
        L[x]=L[y],R[x]=R[y],sum[x]=sum[y];
        return x;
    }
    
    void insert(int&x,int l,int r,int p,int v)
    {
        x=clone(x);
        sum[x]+=v;
        if(l==r)
            return;
        int m=(l+r)/2;
        if(p<=m)
            insert(L[x],l,m,p,v);
        else
            insert(R[x],m+1,r,p,v);
    }
    
    int query(int x,int l,int r,int ql,int qr)
    {
        if(ql<=l&&r<=qr)
            return sum[x];
        int m=(l+r)/2;
        if(qr<=m)
            return query(L[x],l,m,ql,qr);
        if(ql>=m+1)
            return query(R[x],m+1,r,ql,qr);
        return query(L[x],l,m,ql,qr)+query(R[x],m+1,r,ql,qr);
    }
}

int main()
{
//  freopen("BZOJ4771.in","r",stdin);
//  freopen("BZOJ4771.out","w",stdout);
    int t;
    read(t);
    while(t--)
    {
        int n,m;
        read(n),read(m);
        for(int i=1;i<=n;++i)
        {
            read(c[i]);
            to[i]=nx[i]=0;
            id[i]=i;
            S[i].clear();
        }
        for(int i=2;i<=n;++i)
        {
            read(fa[i]);
            nx[i]=to[fa[i]],to[fa[i]]=i;
        }
        dfs1(1);
        dfn=0;
        dfs2(1,1);
        std::sort(id+1,id+n+1,cmp);
        root[0]=0,tot=0;
        int p=1;
        for(int i=1;i<=n;++i)
        {
            root[i]=root[i-1];
            while(p<=n&&dep[id[p]]<=i)
            {
                int x=0,y=0,pcol=c[id[p]];
                it=S[pcol].lower_bound(pos[id[p]]);
                if(it!=S[pcol].end())
                    y=ref[*it];
                if(it!=S[pcol].begin())
                    x=ref[*--it];
                T::insert(root[i],1,n,pos[id[p]],1);
                if(x)
                    T::insert(root[i],1,n,pos[lca(x,id[p])],-1);
                if(y)
                    T::insert(root[i],1,n,pos[lca(id[p],y)],-1);
                if(x&&y)
                    T::insert(root[i],1,n,pos[lca(x,y)],1);
                S[pcol].insert(pos[id[p]]);
                ++p;
            }
        }
        int ans=0;
        while(m--)
        {
            int x=read<int>()^ans,d=read<int>()^ans;
            d=std::min(dep[x]+d,n);
            printf("%d\n",ans=T::query(root[d],1,n,pos[x],pos[x]+siz[x]-1));
        }
    }
    return 0;
}

拓展

这题完全可以改一下,改成序列上的?

BZOJ4771 七彩树

原文:https://www.cnblogs.com/autoint/p/10318783.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!