Codeforces Round #316 D. Tree Requests 树剥分

本文探讨了一种解决树形结构中特定深度节点组成的字符串是否能构成回文的问题,通过层次化编号、计数字母出现次数并利用前缀和优化,实现了高效的查询处理。介绍了几种优化策略,并提供了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
给一颗包含n个节点的树。每个节点上都有一个小写字母。现在给节点v和深度h,求节点v子树的深度为h的点上的字符能否组成回文。
链接:http://codeforces.com/contest/570/problem/D

我的做法:对所有点都重编号,使得同一层的点都是连续的(按层次重编号),然后记录每一层的26个字母出现的次数,对于每个查询u,h,找u下深度为h的左右端点,明显可知道如果字母出现次数为奇数的出现一次以上一定不能组成回文。。
tips:写完后发现根本不用树状数组啊。直接求前缀和就行。。。
tips2:这里有一个优化,只需要求异或和就行了,我们只在意奇偶并不在意具体数量。
tips3:这里还有一个优化,用一个int就可以存下了,因为只有26个字母。
代码:

//author: CHC
//First Edit Time:  2015-08-14 12:02
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
#include <limits>
using namespace std;
typedef long long LL;
const int MAXN=500000+1000;
const int INF = numeric_limits<int>::max();
const LL LL_INF= numeric_limits<LL>::max();
vector <int> dep[MAXN];
int n,m;
int fa[MAXN][21];
int d[MAXN];
int clib(int a,int x){
    int y=0;
    while(x){
        if(x&1)a=fa[a][y];
        x>>=1;
        ++y;
    }
    return a;
}
int C[MAXN];
int p[MAXN];
struct Edge {
    int to,next;
    Edge(){}
    Edge(int _to,int _next):to(_to),next(_next){}
}e[MAXN];
int head[MAXN],tot;
void init(){
    memset(head,-1,sizeof(head));
    tot=0;
}
void AddEdge(int u,int v){
    e[tot]=Edge(v,head[u]);
    head[u]=tot++;
}
int id[MAXN],rid[MAXN];
int Tx[MAXN];
int que[MAXN],front,rear;
int main()
{
    while(~scanf("%d%d",&n,&m)){
        //for(int i=0;i<MAXN;i++)dep[i].clear();
        for(int i=2,x;i<=n;i++){
            scanf("%d",&x);
            p[i]=x;
        }
        init();
        for(int i=n;i>=2;i--){
            AddEdge(p[i],i);
        }
        id[0]=1;
        id[1]=1;
        rid[1]=1;
        dep[1].push_back(1);
        fa[1][0]=1;
        d[1]=1;
        int mm=0;
        front=rear=0;
        que[front++]=1;
        while(front!=rear){
            int i=que[rear++];
            for(int j=head[i];~j;j=e[j].next){
                int v=e[j].to;
                id[v]=++id[0];
                rid[id[0]]=v;
                d[id[v]]=d[id[i]]+1;
                mm=max(d[id[v]],mm);
                dep[d[id[v]]].push_back(id[v]);
                fa[id[v]][0]=id[i];
                for(int k=1;k<=20;k++)
                    fa[id[v]][k]=fa[fa[id[v]][k-1]][k-1];
                que[front++]=v;
            }
        }
        //for(int i=1;i<=mm;i++)
            //sort(dep[i].begin(),dep[i].end());
        memset(C,0,sizeof(C));
        char ch;
        for(int i=1;i<=n;i++){
            scanf(" %c",&ch);
            Tx[id[i]]=(1<<(ch-'a'));
            //C[i]=C[i-1]^(1<<(ch-'a'));
            //printf("h:%d\n",id[i]);
            //Add(id[i],ch-'a'+1,1);
        }
        for(int i=1;i<=n;i++){
            C[i]=C[i-1]^Tx[i];
        }
        int v,h;
        while(m--){
            scanf("%d%d",&v,&h);
            v=id[v];
            if(d[v]>=h){
                puts("Yes");
                continue;
            }
            if(dep[h].empty()){
                puts("Yes");
                continue;
            }
            int sz=dep[h].size();
            int l=0,r=sz-1;
            int ansl=0,ansr=r;
            //puts("bug");
            /*
               if(clib(dep[h][r],h-d[v])<v||clib(dep[h][l],h-d[v])>v){
               puts("Yes");
               continue;
               }
               */
            /*
               puts("");
               for(int i=0;i<(int)dep[h].size();i++){
               printf("%d %d\n",d[dep[h][i]],dep[h][i]);
               }
               */
            while(l<=r){
                int mid=(l+r)>>1;
                //printf("mid:%d\n",mid);
                //int u=lca(dep[h][mid],v);
                int u=clib(dep[h][mid],h-d[v]);
                if(u<v){
                    ansl=mid;
                    l=mid+1;
                }
                else r=mid-1;
            }
            l=0,r=sz-1;
            while(l<=r){
                int mid=(l+r)>>1;
                int u=clib(dep[h][mid],h-d[v]);
                if(u>v){
                    ansr=mid;
                    r=mid-1;
                }
                else l=mid+1;
            }
            //printf("%d %d\n",ansl,ansr);
            //puts("bug");
            //puts("tt");
            if(clib(dep[h][ansl],h-d[v])!=v){
                ansl++;
            }
            if(clib(dep[h][ansr],h-d[v])!=v){
                ansr--;
            }
            if(ansl>sz-1||ansr<0){
                puts("Yes");
                continue;
            }
            if(clib(dep[h][ansl],h-d[v])!=v){
                puts("Yes");
                continue;
            }
            //printf("here:l:%d r:%d\n",ansl,ansr);
            ansl=dep[h][ansl];
            ansr=dep[h][ansr];
            //printf("l:%d r:%d\n",ansl,ansr);
            int ji=0;
            int t=C[ansr]^C[ansl-1];
            while(t>0){
                ++ji;
                t-=t&-t;
            }
            /*
            for(int i=1;i<=26;i++){
                int t=Sum(ansr,i)-Sum(ansl-1,i);
                //printf("%c %d\n",'a'+i-1,t);
                //printf("%d %d\n",Sum(ansr,i),Sum(ansl-1,i));
                if(t&1)++ji;
                else if(t>0)++ou;
            }
            */
            //else puts("No");
            if(ji>1)puts("No");
            else puts("Yes");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值