题目大意:
给你N个点,有M个查询,告诉你每个点的父亲节点的编号。
然后给你一个长度为N的字符串,表示每个点所代表的字符。
让你判断以x为根的子树距离根(1)的深度为y的所有字符是否能够构成一个回文串。
思路(思路来源于网络):
1、判定一堆字符能否构成回文串的方法:对于26个字符中,出现奇数次的字符个数要<=1.
2、我们已知可以O(n)预处理出Dfs序,设定L【u】表示Dfs过程中,进入节点u的时间戳为L【u】.设定R【u】表示Dfs过程中,离开节点u的时间戳为R【u】;
那么如果有点v.其L【u】<=L【v】<=R【u】.那么v这个点就是u的子节点。
那么我们维护一个三维数组vector:Deep【i】【26(j)】,用于存入L【u】:满足u节点深度为i,代表的字符为j
3、那么我们对应一个查询x,y.我们枚举每一种字符,对应查询Deep【y】【i(枚举的字符)】中,大于等于L【x】并且小于等于R【x】的元素的个数。
这里二分实现即可。
注意一些细节,这题卡了常,能用位运算的部分尽量用位运算。
能优化的常数尽量去优化。
Ac代码(1900+ms Ac):
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
char str[500500];
int L[500500];
int R[500500];
vector<int >mp[500500];
vector<int >Deep[500500][28];
int cnt;
void Dfs(int u,int from,int depp)
{
L[u]=++cnt;
Deep[depp][str[u]-'a'+1].push_back(L[u]);
for(int i=0; i<mp[u].size(); i++)
{
int v=mp[u][i];
Dfs(v,u,depp+1);
}
R[u]=cnt;
}
int FindL(int x,int y,int j)
{
int ans=-1;
int l=0;
int r=Deep[y][j].size()-1;
while(r-l>=0)
{
int mid=(l+r)/2;
if(Deep[y][j][mid]>=L[x])
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
return ans;
}
int FindR(int x,int y,int j,int LLL)
{
int ans2=-1;
int l=0;
int r=Deep[y][j].size()-1;
while(r-l>=0)
{
int mid=(l+r)/2;
if(Deep[y][j][mid]<=R[x])
{
ans2=mid;
l=mid+1;
}
else r=mid-1;
}
return ans2;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
cnt=0;
for(int i=2; i<=n; i++)
{
int u;
scanf("%d",&u);
mp[u].push_back(i);
}
scanf("%s",str+1);
Dfs(1,-1,1);
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
int flag=0;
int ttt=0;
for(int j=1; j<=26; j++)
{
int ans=FindL(x,y,j);
if(ans==-1)continue;
int ans2=FindR(x,y,j,ans);
int num=(ans2-ans+1);
if(num&1)
{
ttt++;
if(ttt>1)break;
}
}
if(ttt>1)printf("No\n");
else printf("Yes\n");
}
}
}