【题解】[COCI2020-2021#2] Svjetlo

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} 。

状态转移方程系数:

  1. 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 的状态也要取反。

  1. 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 要多遍历一次。

  1. dp[x][i][2]=min(dp[0][i][1]+dp[y][init[y]][1])

因为是两个 “头” 拼起来,所以直接把序列长度加起来。同样可以翻转 y 的状态。

  1. 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]); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530279.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示