算法分析
树形dp
题目要求从第二个结点开始,每个节点的值都等于上一个节点的值 + 1 % 3 的结果,希望这个序列最长,那么树上问题,对于一个结点来说,经过他最长的序列肯定就是,向子树中的某一侧走到底的最佳答案 + 向子树中的另外一侧走到底的最佳答案
那么最后的答案就是某一侧长度 + 另外一侧长度 - 1
那么按照这个思路思考下去就可以脑补出状态的定义了。
状态表示
从
结点向下搜,满足 子节点为父节点 + 1 % 3 后的结果 的最长长度
(也可以理解为从上往下是递增的
从
结点向下搜,满足 父节点为子节点 + 1 % 3 后的结果 的最长长度
(也可以理解为从上往下是递减的
这样子两侧的链合并时才能是一个方向的
状态转移
void dfs(int now,int fa)
{
dp[now][0] = dp[now][1] = 1;
for(auto ne : g[now])
{
if(ne == fa) continue;
dfs(ne,now);
if((a[now] + 1) % 3 == a[ne])//从父节点看子节点
dp[now][0] = max(dp[now][0],dp[ne][0] + 1);
if((a[ne] + 1) % 3 == a[now])//由子节点看父节点
dp[now][1] = max(dp[now][1],dp[ne][1] + 1);
}
ans = max(ans,dp[now][0] + dp[now][1] - 1);
}
AC Code
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
vector<int>g[N];
int a[N],dp[N][2];
int ans = - 1;
void dfs(int now,int fa)
{
dp[now][0] = dp[now][1] = 1;
for(auto ne : g[now])
{
if(ne == fa) continue;
dfs(ne,now);
if((a[now] + 1) % 3 == a[ne])//从父节点看子节点
dp[now][0] = max(dp[now][0],dp[ne][0] + 1);
if((a[ne] + 1) % 3 == a[now])//由子节点看父节点
dp[now][1] = max(dp[now][1],dp[ne][1] + 1);
}
ans = max(ans,dp[now][0] + dp[now][1] - 1);
}
int main()
{
int n;
cin >> n;
for(int i = 1;i <= n - 1;i ++)
{
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
g[b].push_back(a);
}
for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
dfs(1,0);
printf("%d",ans);
return 0;
}