Codeforces Round #316 (Div. 2)D. Tree Requests

链接:http://codeforces.com/contest/570/problem/D

题意:给定一棵n个节点的树(1为根),每个节点上有一个小写字母,m个询问:给定v,h,问在以v为根的子树中深度为h(相对整棵树的深度)的所有点的字符能否组成回文串。

分析:我们来将题目分解一下,首先我们确定构成回文串的条件,很显然要求“字母个数为奇数的字母小于等于1”,这里我们可以用异或储存偶数为0奇数为1,这样我们只要判断这个二进制数中有几个1就行了。然后我们分析子树v和深度h这两个条件,首先子树v我们可以想到dfs序可以将一颗子树的节点排在一段连续的位置,深度h我们可以想到从根开始bfs的话相同深度的点也会在一起。那么我们可以将这两个条件结合一下得到:bfs的同一层的点是在一段连续的区间内,并且也有同一颗子树中的同一深度的节点也会在一段连续的区间内,那么我们在这段区间中找dfs序在根v的dfs序范围内的点就好了,然后将异或值用前缀异或和优化即可。详见代码。O(n+m*logn)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=500100;
const int MAX=151;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const ll INF=10000000010;
typedef double db;
typedef unsigned long long ull;
int tot,u[N],v[N],pre[N];
void add(int x,int y) {
    v[tot]=y;pre[tot]=u[x];u[x]=tot++;
}
char s[N];
int H,ti,l[N],r[N],de[N];
void dfs(int x,int y) {
    l[x]=++ti;de[x]=de[y]+1;
    H=max(H,de[x]);
    for (int i=u[x];i!=-1;i=pre[i]) dfs(v[i],x);
    r[x]=ti;
}
int d[N],w[N],bl[N],br[N],sum[N];
void bfs() {
    int i,b=1,k=1,deep;
    memset(br,0,sizeof(br));
    memset(bl,0x3f,sizeof(bl));
    d[1]=w[1]=bl[1]=br[1]=1;
    while (b<=k) {
        for (i=u[d[b]];i!=-1;i=pre[i]) {
            k++;d[k]=v[i];w[k]=l[v[i]];deep=de[v[i]];
            bl[deep]=min(bl[deep],k);
            br[deep]=max(br[deep],k);
        }
        b++;
    }
}
int getl(int x,int y,int z) {
    int mid=(y+z)/2;
    while (y+1<z)
    if (w[mid]>=x) { z=mid;mid=(y+z)/2; }
    else { y=mid;mid=(y+z)/2; }
    return z;
}
int getr(int x,int y,int z) {
    int mid=(y+z)/2;
    while (y+1<z)
    if (w[mid]<=x) { y=mid;mid=(y+z)/2; }
    else { z=mid;mid=(y+z)/2; }
    return y;
}
int cala(int x) {
    int ret=0;
    while (x) { ret++;x-=x&(-x); }
    return ret;
}
int main()
{
    int i,n,m,x,y,L,R;
    scanf("%d%d", &n, &m);
    tot=0;memset(u,-1,sizeof(u));
    for (i=1;i<n;i++) {
        scanf("%d", &x);add(x,i+1);
    }
    scanf("%s", s);
    H=ti=de[1]=sum[0]=0;
    dfs(1,1);bfs();
    for (i=1;i<=n;i++) sum[i]=sum[i-1]^(1<<(s[d[i]-1]-'a'));
    while (m--) {
        scanf("%d%d", &x, &y);
        if (de[x]>y||l[x]==r[x]||y>H) printf("Yes\n");
        else {
            L=getl(l[x],bl[y]-1,br[y]);
            R=getr(r[x],bl[y],br[y]+1);
            if (cala(sum[R]^sum[L-1])<=1) printf("Yes\n");
            else printf("No\n");
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值