【BZOJ 4771】七彩树

一直TLE的原因竟然是数组开太大了导致\(memset\)清空耗时超限,亏我还调了1天啊(T^T)


题目大意

给定一颗树,每个节点都有一个颜色,要求多次询问某个节点\(x\)的子树中深度不超过\(d\)的节点中,有多少种不同的颜色,强制在线。

概述

首先,我们可以考虑两个相同颜色的节点对答案的贡献。

很显然,他对他们自己所有的祖先的答案贡献都为\(1\),在他们\(lca\)及以上的地方被重复计算了一次,所以说都需要减去\(1\);

然后考虑对于相同颜色的节点,对相同颜色节点的\(dfs\)序维护一个set,每一次去寻找他的前趋与后继,进行我们刚刚抽离出来讨论的操作,这样的话就可以很好的解决这个问题。

那应该怎么维护呢?很显然,如果没有距离限制,在每个结点处维护一颗线段树足矣。如果有距离限制,受『谈笑风生』一题的启发,我们可以以深度来维护一颗主席树,这只需要我们在进行上一段讨论的操作时按照深度顺序进行操作,对原操作是毫无影响的。

具体见代码,常数略大:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct cc{
    int to,nex;
}e[maxn*2];
int head[maxn],tot;
int a[maxn],b[maxn];
int ls[maxn*50],rs[maxn*50],sum[maxn*50],cnt,rt[maxn];
inline int read()
{
    int ret=0,f=1;  char gc=getchar();
    while(gc<'0'||gc>'9') {if(gc=='-')f=-f;   gc=getchar();}
    while(gc>='0'&&gc<='9')   ret=ret*10+gc-'0',gc=getchar();
    return ret*f;
}
void update(int &u,int pos,int l,int r,int t,int num)
{
    u=++tot;
    sum[u]=sum[pos]+num,rs[u]=rs[pos],ls[u]=ls[pos];
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(t<=mid) update(ls[u],ls[pos],l,mid,t,num);
    else update(rs[u],rs[pos],mid+1,r,t,num);
}
int query(int ll,int rr,int l,int r,int u)
{
    if((ll<=l&&r<=rr)||(!u)) return sum[u];
    int mid=(l+r)>>1;
    if (rr<=mid) return query(ll,rr,l,mid,ls[u]);
    if(ll>mid)return query(ll,rr,mid+1,r,rs[u]);
    return query(ll,rr,l,mid,ls[u])+query(ll,rr,mid+1,r,rs[u]);
}
void add(int u,int v)
{
    ++cnt;
    e[cnt].to=v;
    e[cnt].nex=head[u];
    head[u]=cnt;
} 
int fa[maxn][19],col[maxn],dep[maxn],low[maxn],dfn[maxn],tim,id[maxn],p[maxn],lg[maxn];
int n,m;
bool cmp(int x,int y)
{
    return dep[x]<dep[y];
}//原来可以不用打包啊,震惊
void dfs(int u)
{
    ++tim,dfn[u]=tim,p[tim]=u;
    for(int i=head[u];i!=-1;i=e[i].nex)
        dep[e[i].to]=dep[u]+1,dfs(e[i].to);
    low[u]=tim;
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=lg[dep[x]-dep[y]];i>=0;--i) 
        if(dep[fa[x][i]]>=dep[y]) 
            x=fa[x][i];
    if(x==y) return x;
    for(int i=lg[dep[x]];i>=0;--i)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
} 
set<int> g[maxn];
void init()
{
    memset(fa,0,sizeof fa);
    tim=cnt=tot=0;
    memset(head,-1,sizeof head);
    memset(rt,0,sizeof rt);
}
set<int>::iterator it;
int main()
{ 
    int T;
    T=read();
    for(int _=1;_<=T;++_)
    {
        int aa,bb;
        init();
        n=read(),m=read();
        for(int i=1;i<=n;++i) col[i]=read(),id[i]=i,g[i].clear(),lg[i]=lg[i>>1]+1;
        for(int i=2;i<=n;++i) fa[i][0]=read(),add(fa[i][0],i);
        dep[1]=1,dfs(1);
        for(int j=1;j<=18;++j)
            for(int i=1;i<=n;++i)
                fa[i][j]=fa[fa[i][j-1]][j-1];   
        sort(id+1,id+n+1,cmp);
        for(int i=1;i<=n;++i)
        {
            int now=id[i];aa=bb=0;
            it=g[col[now]].lower_bound(dfn[now]);
            update(rt[dep[now]],rt[dep[id[i-1]]],1,n,dfn[now],1);
            if(it!=g[col[now]].end())
                bb=p[(*it)],update(rt[dep[now]],rt[dep[now]],1,n,dfn[lca(now,bb)],-1);
            if(it!=g[col[now]].begin())
                --it,aa=p[(*it)],update(rt[dep[now]],rt[dep[now]],1,n,dfn[lca(now,aa)],-1);
            if(aa&&bb) update(rt[dep[now]],rt[dep[now]],1,n,dfn[lca(aa,bb)],1);
            g[col[now]].insert(dfn[now]);
        }
        int lst=0;
        for(int i=1;i<=m;++i)
        {
            aa=read(),bb=read();
            aa^=lst,bb^=lst;
            lst=query(dfn[aa],low[aa],1,n,rt[min(dep[aa]+bb,dep[id[n]])]);
            printf("%d\n",lst);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/HenryHuang-Never-Settle/p/11000369.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值