Codeforces Round #316 (Div. 2) D 【dfs序+二分】

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

题意:给你一棵n个节点树,树的每个节点少有一个字母,然后m个询问,问节点a的子树中深度(以1为根)为b的节点上的字母能否组成回文串。

分析:判断回文串,我们只要找到字母为奇数个的个数大于1就不能组成回文,我们可以用异或求出,用前缀和优化,在dfs的时候深度一样的节点都是按照顺序便利的,我们可以用vector记录当前深度的异或和。

然后我们找子节点的时候肯定不能暴力,我们可以可以用dfs序的到子节点与根的关系,子节点的左dfs序一定大于根,右dfs序小于根,然后就可以二分求出了。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 500005
#define Mm 5000005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
struct edge {
    int v,next;
}e[Mm];
int tot,head[Mn];
void addedge(int u,int v) {
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot++;
}
int ldfn[Mn],rdfn[Mn];
int dep[Mn];
vector<pair<int,int> > ve[Mn];
int dfnclock=0;
string s;
void dfs(int u,int de) {
    dep[u]=de;
    ldfn[u]=++dfnclock;
    ve[de].push_back(make_pair(dfnclock,ve[de].back().second^(1<<(s[u-1]-'a'))));
    for(int i=head[u];~i;i=e[i].next) {
        int v=e[i].v;
        dfs(v,de+1);
    }
    rdfn[u]=++dfnclock;
}
int getbinum(int x) {
    int re=0;
    while(x) {
        re++;x-=x&(-x);
    }
    return re;
}
void init() {
    tot=0;
    CLR(head,-1);
}
int main() {
    int n,m,x;
    init();
    cin>>n>>m;
    for(int i=2;i<=n;i++) {
        scanf("%d",&x);
        addedge(x,i);
    }
    cin>>s;
    for(int i=1;i<=n;i++)
        ve[i].push_back(make_pair(0,0));
    dfs(1,1);
    for(int i=1;i<=m;i++) {
        int u,h;
        scanf("%d%d",&u,&h);
        if(dep[u]>=h) {
            printf("Yes\n");
            continue;
        }
        int lv=lower_bound(ve[h].begin(),ve[h].end(),make_pair(ldfn[u],-1))-ve[h].begin()-1;//找到的就是最左端,但是编号从1开始所以减一。
        int rv=lower_bound(ve[h].begin(),ve[h].end(),make_pair(rdfn[u],-1))-ve[h].begin()-1;//找到大于的,减一就是子树的最右断,
        if(getbinum(ve[h][lv].second^ve[h][rv].second)<=1) {
           printf("Yes\n");
        } else printf("No\n");
    }
    return 0;
}


你可能感兴趣的:(Codeforces Round #316 (Div. 2) D 【dfs序+二分】)