题意:一棵树,每个节点有个权值k[i]和颜色c[i],定义以节点u为根节点的序列为“好的序列”条件是:能够找到一条有序链,该链以u节点开始,链的长度为k[i]+1,链的成员颜色均为c[i],对于链上第j个元素,j-1和j+1个元素必须是其祖先和子孙,问对每个节点求出它的好的序列数。
思路:官方题解 树形dp,dp[i][j]为i颜色j长度的链数,ans[u]为u节点的好序列数,那么ans[u] = dp[c[u]][k[i]-1],这里dp没有保存当前结点,因为搜索到节点v时,先记录s = dp[c[v]][k[v]-1],回溯时减回这个s就能保证结果是从子树得到了。
//reference: Pawel
# include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5+30;
const int mod = 1e9+7;
vector<int>dp[maxn], v[maxn];
int club[maxn], cnt[maxn], level[maxn];
int ans[maxn];
void dfs(int u)
{
int s = (level[u]>0&&level[u]<cnt[club[u]])?dp[club[u]][level[u]-1]:0;
for(auto to : v[u])
dfs(to);
if(level[u] >= cnt[club[u]]) return;
if(level[u] == 0)
ans[u] = 1;
else
ans[u] = (dp[club[u]][level[u]-1] - s + mod)%mod;
dp[club[u]][level[u]] += ans[u];
dp[club[u]][level[u]] %= mod;
}
int main()
{
int t, x, n;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&x);
for(int i=0; i<n; ++i)
{
v[i].clear();
dp[i].clear();
ans[i] = cnt[i] = 0;
}
for(int p, i=1; i<n; ++i)
{
scanf("%d",&p);
v[p].push_back(i);
}
for(int i=0; i<n; ++i)
{
scanf("%d",&club[i]);
++cnt[club[i]];
}
for(int i=0; i<n; ++i)
scanf("%d",&level[i]);
for(int i=0; i<n; ++i)
dp[i] = vector<int>(cnt[i]);
dfs(0);
for(int i=0; i<n; ++i)
printf("%d\n",ans[i]);
}
return 0;
};