570D Codeforces Round #316 (Div. 2) D(dfs序,时间戳,二分

题目:一棵树上每个节点有个字符值,询问每个节点的深度为h的子节点的字符是否能组成一个回文串。

思路:首先是奇妙的dfs序和时间戳,通过记录每个节点的dfs进出时间,可以发现某个节点的子节点的进出时间均在该节点的进出时间范围内(这是很直观的dfs的性质),这样可以把树形结构转变为线性结构,方便进行各种处理。dfs一遍处理时间戳,每个节点的深度,并记录每个深度的节点都有哪些,此时每个深度的节点就是排好序的。然后对于一个询问,可以利用二分查找确定子节点在该层的哪一段。对于每一层,预先处理每个字符的前缀和,就可以做到O(1)查询区间数量。由于这题只要求奇偶性,故只需要记录01,又因为字符集为26,可以直接用int类型压位。

ps:二分直接用的stl的,结果反而写得像一坨翔一样= =

#include <bits/stdc++.h>
#define pb push_back
#define se second
#define fs first
#define sq(x) (x)*(x)
#define eps 0.000000001
#define LB lower_bound
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=5e5+3000;
int n,m;
vector<int> G[maxv];
vector<int> D[maxv];
vector<int> num[maxv];
int deep[maxv];
int in[maxv],out[maxv];
char s[maxv];
int clo=0;
void dfs(int v,int d,int f){
    D[d].pb(v);
    deep[v]=d;
    in[v]=++clo;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==f) continue;
        dfs(u,d+1,v);
    }
    out[v]=++clo;
}
void cul(){
    for(int i=1;i<=n;i++){
        if(D[i].size()==0){
            break;
        }
        num[i].pb(1<<(s[D[i][0]-1]-'a'));
        for(int j=1;j<D[i].size();j++){
            int c=s[D[i][j]-1]-'a';
            num[i].pb(num[i].back()^(1<<c));
        }
    }
}
bool cmpl(const int &va,const int &vb){return in[va]<vb;}
bool cmpr(const int &va,const int &vb){return out[va]<vb;}
int cas=0;
int main(){
//    freopen("/home/files/CppFiles/in","r",stdin);
    cin>>n>>m;
    for(int i=2;i<=n;i++){
        int p;
        scanf("%d",&p);
        G[p].pb(i);
    }
    scanf("%s",s);
    dfs(1,1,-1);
    cul();
    while(m--){
        int v,h;
        scanf("%d%d",&v,&h);
        int l=in[v],r=out[v],sd=h;
        if(deep[v]>h){
            puts("Yes");
            continue;
        }
        vector<int>::iterator itl=LB(D[sd].begin(),D[sd].end(),l,cmpl);
        vector<int>::iterator itr=LB(D[sd].begin(),D[sd].end(),r,cmpr);
        if(itr!=D[sd].begin()&&((itr!=D[sd].end()&&out[*itr]>r)||itr==D[sd].end())) itr--;
        if(itl!=D[sd].end()&&itr!=D[sd].end()&&in[*itl]>=l&&out[*itr]<=r){
            int dl=itl-D[sd].begin();
            int dr=itr-D[sd].begin();
            int nu=(dl>0?num[sd][dl-1]:0);
            if(nu^num[sd][dr]){
                if(__builtin_popcount(nu^num[sd][dr])<2)
                    puts("Yes");
                else{
                    puts("No");
                }
            }else{
                puts("Yes");
            }
        }else{
            puts("Yes");
            continue;
        }
    }
}
View Code

 

转载于:https://www.cnblogs.com/Cw-trip/p/4731543.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值