solution:
神仙状态。
考点:树形dp + 思维。
路径是可以重复的,简单地树形 dp 可能难以处理,考虑路径的拼接。
设 d p [ x ] [ i ] [ j ] dp[x][i][j] dp[x][i][j] 表示第 x 个点的子树内(除了自己)的奇偶性已经满足,i 记录第 x 个点的奇偶性是否满足,且子树内(包括自己)的路径端点数有 j 个的最短路径长度,其中 i∈{0,1},j∈{0,1,2} 。
状态转移方程系数:
dp[x][i][j]=min(dp[0][i^1][j]+dp[y][init[y]][0]+1)
类似的如果翻转 y 的状态,那么:dp[x][i][j]=min(dp[0][i][j]+dp[y][rev[y]][0]+3)
解释一下,因为没有 “头”,所以 x 要多遍历一次,同时 i 的状态也要取反。
dp[x][i][2]=min(dp[0][i^1][0]+dp[y][init[y]][2]+1)
同样翻转 y 的状态,能得到类似的转移:dp[x][i][2]=min(dp[0][i][0]+dp[y][rev[y]][2]+3)
同样是因为没有 “头”,所以 x 要多遍历一次。
dp[x][i][2]=min(dp[0][i][1]+dp[y][init[y]][1])
因为是两个 “头” 拼起来,所以直接把序列长度加起来。同样可以翻转 y 的状态。
dp[x][i][1]=min(dp[0][i][0]+dp[y][init[y]][1])
这里和第一条转移有区别,第一条是因为 x 有 ”头“,所以要回到 x , x 多遍历一次;这个是 x 没有 ”头“,所以直接拼起来即可。可以翻转 x 的状态。
最后输出 dp[x][2][1]
即可。注意 init[x]=1
。特判空子树,否则会 wa 。
代码部分借鉴的 k c z n o l kcznol kcznol 大佬。
#include<bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
using namespace std;
const int mx=5e5+5;
int init[mx],rev[mx];
int n,dp[mx][2][3],c1[mx];
char str[mx];
vector<int> g[mx];
template <typename T> void chmin(T &x, const T &y) {
if (x > y)
x = y;
}
void dfs1(int x,int fr) {
// printf("%d\n",x);
for(int i=0;i<=1;i++) {
for(int j=0;j<=2;j++) {
dp[x][i][j]=i?1:1e9;
}
}
c1[x]=init[x];
for(auto y:g[x]) {
if(y==fr) continue;
dfs1(y,x);
c1[x]+=c1[y];
if(c1[y]==0) continue;
memcpy(dp[0],dp[x],sizeof dp[x]);
memset(dp[x],0x3f,sizeof dp[x]);
for(int i=0;i<=1;i++) {
chmin(dp[x][i][2],dp[0][i][0]+dp[y][rev[y]][2]+1);
chmin(dp[x][i][2],dp[0][i^1][0]+dp[y][init[y]][2]+3);
chmin(dp[x][i][2],dp[0][i][1]+dp[y][init[y]][1]);
chmin(dp[x][i][2],dp[0][i^1][1]+dp[y][rev[y]][1]+2);
chmin(dp[x][i][1],dp[0][i][0]+dp[y][init[y]][1]);
chmin(dp[x][i][1],dp[0][i^1][0]+dp[y][rev[y]][1]+2);
for(int j=0;j<=2;j++) {
chmin(dp[x][i][j],dp[0][i^1][j]+dp[y][init[y]][0]+1);
chmin(dp[x][i][j],dp[0][i][j]+dp[y][rev[y]][0]+3);
}
}
}
}
int main() {
// freopen("data.in","r",stdin);
scanf("%d",&n);
scanf("%s",str);
for(int i=1;i<=n;i++) init[i]=str[i-1]=='0',rev[i]=init[i]^1;
for(int i=1;i<=n-1;i++) {
int u,v; scanf("%d%d",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
int x=1; while(!init[x]) ++x;
dfs1(x,0);
// for(int i=1;i<=n;i++) {
// for(int j=0;j<=1;j++) {
// for(int k=0;k<=2;k++) {
// printf("dp[%d][%d][%d]=%d\n",i,j,k,dp[i][j][k]);
// }
// }
// }
printf("%d",dp[x][1][2]);
}