题目链接:点击查看
题目大意:给定一个以1为根的n个节点的树,每个点上有一个字母(a−z),每个点的深度定义为该节点到1号节点路径上的点数,每次询问a,b查询以a为根的子树内深度为b的节点上的字母重新排列之后是否能构成回文串
题目分析:因为是关于每个子树的询问,所以可以试着用树上启发式合并考虑,具体问题需要具体分析,对于这个题目而言,主要是判断以某个结点为根节点的子树中,深度为d的结点中,出现次数为奇数的字母的个数必须小于等于1才行,那么我们只需实时维护每个字母的出现次数就好了,具体实现看代码吧
最后有个细节需要说一下,就是在读入字母的时候,如果用getchar或scanf读的话,必须要自己写一个Getchar函数才行,不然数据中会有n=1的情况,这个时候需要连续读入两个回车,不然就会RE
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=5e5+100;
int w[N],deep[N],num[N],son[N],cnt[N][26];
bool vis[N],ans[N];
vector<int>node[N];
vector<pair<int,int>>Q[N];//<id,deep>
void dfs_son(int u,int dep)
{
son[u]=-1;
num[u]=1;
deep[u]=dep;
for(auto v:node[u])
{
dfs_son(v,dep+1);
num[u]+=num[v];
if(son[u]==-1||num[v]>num[son[u]])
son[u]=v;
}
}
void cal(int u,int val)
{
cnt[deep[u]][w[u]]+=val;
for(auto v:node[u])
{
if(vis[v])
continue;
cal(v,val);
}
}
void dfs(int u,int keep)
{
for(auto v:node[u])
{
if(v==son[u])
continue;
dfs(v,0);
}
if(son[u]!=-1)
{
dfs(son[u],1);
vis[son[u]]=true;
}
cal(u,1);
for(auto i:Q[u])
{
int id=i.first;
int d=i.second;
int sum=0;
for(int j=0;j<26;j++)
if(cnt[d][j]&1)
sum++;
if(sum>1)
ans[id]=false;
else
ans[id]=true;
}
if(son[u]!=-1)
vis[son[u]]=false;
if(!keep)
cal(u,-1);
}
char Getchar()
{
char ch;
while(ch=getchar())
if(ch!='\n'&&ch!=' ')
return ch;
}
int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);
int n,m;
scanf("%d%d",&n,&m);
for(int i=2;i<=n;i++)
{
int fa;
scanf("%d",&fa);
node[fa].push_back(i);
}
char ch;
for(int i=1;i<=n;i++)
{
ch=Getchar();
w[i]=ch-'a';
}
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
Q[a].push_back(make_pair(i,b));
}
dfs_son(1,1);
dfs(1,1);
for(int i=1;i<=m;i++)
{
if(ans[i])
puts("Yes");
else
puts("No");
}
return 0;
}