树形 dp

Posted on 2026-08-20 12:22  暮光落阳  阅读(2)  评论(0)    收藏  举报

树形 dp

「SHOI2017」摧毁「树状图」

转移式可以复杂,但也只有转移式复杂

开一个 \([u][4][4]\) 数组,记录两条路线的状态,\(0\) 表示无路线,\(1\) 表示可延伸的路线,\(2\) 表示存在路线但不可延伸,\(4\) 表示路线完全在子树中结束。类似树上背包,dfs逐子节点处理,从而推36个从子节点到缓存数组的式子,再推到15种父节点的式子(\([1][1]\) 不存在)。只是非常容易遗漏情况。

transform
0,0	=0,0
0,1	=0,0 +son
	=0,1 +son-1
0,2	=0,0 +son
	=0,1 +son-1
	=0,1-0,1 +son-2
0,3 =0,2
	=0,3
1,2 =0,0 +son
	=0,1 +son-1
	=1,0 +son-1
	=0,1-0,1 +son-2
	=0,1-1,0 +son-2
	=0,1-0,1-1,0 +son-3
1,3	=0,2 +son-1
	=0,3 +son
	=1,2 +son-1
	=1,3 +son-1
	=0,2-1,0 +son-2
	=0,3-1,0 +son-1
2,2 =0,0 +son
	=0,1 +son-1
	=0,1-1,0 +son-2
	=0,1-0,1-1,0 +son-3
	=0,1-0,1-1,0-1,0 +son-4
2,3	=0,2 +son-1
	=0,3 +son
	=1,2 +son-1
	=1,3 +son-1
	=0,2-1,0 +son-2
	=0,3-1,0 +son-1
	=1,2-1,0 +son-2
	=1,3-1,0 +son-2
	=0,2-1,0-1,0 +son-3
	=0,3-1,0-1,0 +son-2
3,3	=2,2
	=2,3
	=3,3
	=0,2-2,0
	=0,2-3,0
	=0,3-3,0
Code
#include <bits/stdc++.h>
#define int long long

using namespace std;

int read(){
    int i;
    scanf("%lld",&i);
    return i;
}

int n,T,x;
int head[100010],to[200010],nex[200010],edge=0;
vector<array<array<int,4>,4> >dp;

void dfs(int u,int fa){
    int f[40];
    f[0]=0;
    for(int i=1;i<=36;i++)f[i]=-2147483648;
    int son=0;
    for(int te=head[u],v;te;te=nex[te]){
        v=to[te];
        if(v==fa)continue;
        son++;
        dfs(v,u);
        f[36]=max({dp[v][1][0]+f[5],dp[v][1][2]+f[4],f[36]});
        f[35]=max({dp[v][0][1]+f[8],dp[v][2][1]+f[1],f[35]});
        f[34]=max({dp[v][0][3]+f[11],dp[v][3][0]+f[3],f[34]});
        f[33]=max({dp[v][0][3]+f[7],dp[v][2][0]+f[3],f[33]});
        f[32]=max({dp[v][0][2]+f[11],dp[v][3][0]+f[2],f[32]});
        f[31]=max({dp[v][1][0]+f[6],dp[v][1][3]+f[4],f[31]});
        f[30]=max({dp[v][0][1]+f[12],dp[v][3][1]+f[1],f[30]});
        f[29]=max({dp[v][0][2]+f[7],dp[v][2][0]+f[2],f[29]});
        f[28]=max({dp[v][0][1]+f[23],dp[v][1][0]+f[22],f[28]});
        f[27]=max({dp[v][0][3]+f[19],dp[v][1][0]+f[18],f[27]});
        f[26]=max({dp[v][0][2]+f[19],dp[v][1][0]+f[17],f[26]});
        f[25]=max({dp[v][0][1]+f[21],dp[v][3][0]+f[15],f[25]});
        f[24]=max({dp[v][0][1]+f[20],dp[v][2][0]+f[15],f[24]});
        f[23]=max({dp[v][0][1]+f[19],dp[v][1][0]+f[16],f[23]});
        f[22]=max({dp[v][0][1]+f[16],dp[v][1][0]+f[15],f[22]});
        f[21]=max({dp[v][0][1]+f[11],dp[v][3][0]+f[1],f[21]});
        f[20]=max({dp[v][0][1]+f[7],dp[v][2][0]+f[1],f[20]});
        f[19]=max(dp[v][1][0]+f[4],f[19]);
        f[18]=max({dp[v][0][3]+f[4],dp[v][1][0]+f[3],f[18]});
        f[17]=max({dp[v][0][2]+f[4],dp[v][1][0]+f[2],f[17]});
        f[16]=max({dp[v][0][1]+f[4],dp[v][1][0]+f[1],f[16]});
        f[15]=max(dp[v][0][1]+f[1],f[15]);
        f[14]=max(dp[v][3][3],f[14]);
        f[13]=max(dp[v][3][2],f[13]);        
        f[12]=max(dp[v][3][1],f[12]);
        f[11]=max(dp[v][3][0],f[11]);
        f[10]=max(dp[v][2][3],f[10]);
        f[9]=max(dp[v][2][2],f[9]);        
        f[8]=max(dp[v][2][1],f[8]);
        f[7]=max(dp[v][2][0],f[7]);
        f[6]=max(dp[v][1][3],f[6]);
        f[5]=max(dp[v][1][2],f[5]);
        f[4]=max(dp[v][1][0],f[4]);
        f[3]=max(dp[v][0][3],f[3]);
        f[2]=max(dp[v][0][2],f[2]);
        f[1]=max(dp[v][0][1],f[1]);
    }
    dp[u][0][0]=f[0];
    dp[u][0][1]=max(f[0]+son,f[1]+son-1);
    dp[u][0][2]=max({f[0]+son,f[1]+son-1,f[15]+son-2});
    dp[u][0][3]=max(f[2],f[3]);
    dp[u][1][0]=max(f[0]+son,f[4]+son-1);
    dp[u][1][2]=max({f[0]+son,f[1]+son-1,f[4]+son-1,f[15]+son-2,f[16]+son-2,f[22]+son-3});
    dp[u][1][3]=max({f[2]+son-1,f[3]+son,f[5]+son-1,f[6]+son-1,f[17]+son-2,f[18]+son-1});
    dp[u][2][0]=max({f[0]+son,f[4]+son-1,f[15]+son-2});
    dp[u][2][1]=max({f[0]+son,f[1]+son-1,f[4]+son-1,f[16]+son-2,f[19]+son-2,f[23]+son-3});
    dp[u][2][2]=max({f[0]+son,f[1]+son-1,f[4]+son-1,f[16]+son-2,f[22]+son-3,f[23]+son-3,f[28]+son-4});
    dp[u][2][3]=max({f[2]+son-1,f[3]+son,f[5]+son-1,f[6]+son-1,f[17]+son-2,f[18]+son-1,f[36]+son-2,f[31]+son-2,f[26]+son-3,f[27]+son-2});
    dp[u][3][0]=max(f[7],f[11]);
    dp[u][3][1]=max({f[7]+son-1,f[11]+son,f[8]+son-1,f[12]+son-1,f[20]+son-2,f[21]+son-1});
    dp[u][3][2]=max({f[7]+son-1,f[11]+son,f[8]+son-1,f[12]+son-1,f[20]+son-2,f[21]+son-1,f[35]+son-2,f[30]+son-2,f[24]+son-3,f[25]+son-2});
    dp[u][3][3]=max({f[9],f[10],f[13],f[14],f[29],f[32],f[33],f[34]});
}

void init(){
    n=read(),edge=0;
    for(int i=1;i<=2*x;i++)read();
    dp.resize(n+10,{});
    memset(head+1,0,sizeof(int)*n);
    for(int i=1,u,v;i<n;i++){
        u=read(),v=read();
        nex[++edge]=head[u];
        to[head[u]=edge]=v;
        nex[++edge]=head[v];
        to[head[v]=edge]=u;
    }
    dfs(1,0);
    printf("%lld\n",max({dp[1][1][2],dp[1][1][3],dp[1][2][1],dp[1][2][2],dp[1][2][3],dp[1][3][1],dp[1][3][2],dp[1][3][3]+1}));
    return;
}

signed main(){
    T=read(),x=read();
    for(int i=1;i<=T;i++)init();
    return 0;
}

ABC416F Paint Tree 2

不得不说,相对于邻近的题目,这一道非常纯粹。没有树上背包,也没有换根。

每个点仅能包含于一条路径中,因而状态可分为此节点 未被选中/与一个子结点相连(可向上传递)/与两个子结点相连(不可向上传递)

操作次数与染色状态各一维,依然逐子节点处理。

可以通过控制更新顺序避免覆盖需使用的节点信息。

Code
#include <bits/stdc++.h>
#define int long long
using namespace std;

int read(){
    int i;
    scanf("%lld",&i);
    return i;
}

int n,V;
int head[200010],to[400010],nex[400010],edge=0;
int w[200010];
int dp[200010][7][3];

void dfs(int u,int fa){
    for(int i=0;i<=V+1;i++)dp[u][i][0]=dp[u][i][1]=dp[u][i][2]=-21474836480000000000;//注意数据范围!!!
    dp[u][1][1]=w[u],dp[u][0][0]=0;
    for(int te=head[u],v;te;te=nex[te]){
        v=to[te];
        if(v==fa)continue;
        dfs(v,u);
        for(int m=V;m>=1;m--){
            for(int k=m;k>=0;k--){//倒叙枚举,防止覆盖信息
                tie(dp[u][m][0],dp[u][m][1],dp[u][m][2])=
                make_tuple(max({dp[u][k][0]+dp[v][m-k][0],dp[u][m][0]}),
                max({dp[u][k][0]+dp[v][m-k][1]+w[u],dp[u][k][1]+dp[v][m-k][0],dp[u][m][1]}),
                max({dp[u][k][2]+dp[v][m-k][0],dp[u][k][1]+dp[v][m-k+1][1],dp[u][m][2]}));    
            }
        }
    }
    for(int m=1;m<=V;m++)dp[u][m][0]=max({dp[u][m][0],dp[u][m][1],dp[u][m][2]});
}

int init(){
    n=read(),V=read();
    for(int i=1;i<=n;i++)w[i]=read();
    for(int i=1,u,v;i<n;i++){
        u=read(),v=read();
        nex[++edge]=head[u];
        to[head[u]=edge]=v;
        nex[++edge]=head[v];
        to[head[v]=edge]=u;
    }
    dfs(1,0);
    int ans=0;
    for(int i=0;i<=V;i++)ans=max(dp[1][i][0],ans);
    printf("%lld",ans);
    return 2;
}

int Mouska_Mouska=init();

signed main(){
    return 0;
}


ARC179D Portable Gate

转移起始位置的换根dp。

显然,依照dfs序遍历一定不劣,因而可转化为子树中的问题。

状态:\(dp[i][j][k]\) :在\(i\)子树中的最小步数 \(i\) :当前位置,\(j\) :是否有门,\(k\) :是否返回\(i\)

\[dp[u][1][0]=\min(dp[u][1][1]+min(dp[v][1][0]+1,dp[v][0][0]+1),dp[u][1][0]+min(dp[v][1][1]+2,dp[v][0][0]+1))\\ dp[u][0][0]=\min(dp[u][0][1]+dp[v][0][0]+1,dp[u][0][0]+dp[v][0][1]+2)\\ dp[u][1][1]=dp[u][1][1]+min(dp[v][1][1]+2,dp[v][0][0]+1)\\ dp[u][0][1]dp[u][0][1]+dp[v][0][1]+2)\\ \]

换根可以用前后缀和优化为线性复杂度。

Code
#include <bits/stdc++.h>
#define int long long
using namespace std;

int read(){
int i;
scanf("%lld",&i);
return i;
}

int n;
int head[200010],to[400010],nex[400010],edge=0;
array<array<array<int,2>,2>,200010> dp;
int ans=2147483648999999,son[200010];

void dfs(int u,int fa){
for(int te=head[u],v;te;te=nex[te]){
v=to[te];
son[u]++;
if(v==fa)continue;
dfs(v,u);
tie(dp[u][1][0],dp[u][0][0],dp[u][1][1],dp[u][0][1])=make_tuple(
min(dp[u][1][1]+min(dp[v][1][0]+1,dp[v][0][0]+1),dp[u][1][0]+min(dp[v][1][1]+2,dp[v][0][0]+1)),
min(dp[u][0][1]+dp[v][0][0]+1,dp[u][0][0]+dp[v][0][1]+2),
dp[u][1][1]+min(dp[v][1][1]+2,dp[v][0][0]+1),
dp[u][0][1]+dp[v][0][1]+2);
}
} 

void dfs2(int u,int fa){
vector<array<array<int,2>,2> >pre,pos;
pre.resize(son[u]+2,{}),pos.resize(son[u]+2,{});
for(int te=head[u],v,k=0;te;te=nex[te]){
v=to[te];
k++;
pre[k]=pos[k]=dp[v];
}
for(int i=1;i<=son[u];i++){
tie(pre[i][1][0],pre[i][0][0],pre[i][1][1],pre[i][0][1])=make_tuple(
min(pre[i-1][1][0]+min(pre[i][1][1]+2,pre[i][0][0]+1),pre[i-1][1][1]+min(pre[i][1][0]+1,pre[i][0][0]+1)),
min(pre[i-1][0][0]+pre[i][0][1]+2,pre[i-1][0][1]+pre[i][0][0]+1),
pre[i-1][1][1]+min(pre[i][1][1]+2,pre[i][0][0]+1),
pre[i-1][0][1]+pre[i][0][1]+2);
}
for(int i=son[u];i>=1;i--){
tie(pos[i][1][0],pos[i][0][0],pos[i][1][1],pos[i][0][1])=make_tuple(
min(pos[i+1][1][0]+min(pos[i][1][1]+2,pos[i][0][0]+1),pos[i+1][1][1]+min(pos[i][1][0]+1,pos[i][0][0]+1)),
min(pos[i+1][0][0]+pos[i][0][1]+2,pos[i+1][0][1]+pos[i][0][0]+1),
pos[i+1][1][1]+min(pos[i][1][1]+2,pos[i][0][0]+1),
pos[i+1][0][1]+pos[i][0][1]+2);
}
//printf("%lld %lld %lld %lld %lld\n",u,pos[1][1][0],pos[1][1][1],pos[1][0][0],pos[1][0][1]);
ans=min({pos[1][1][0],pos[1][1][1],pos[1][0][0],pos[1][0][1],ans});
for(int te=head[u],v,k=0;te;te=nex[te]){
v=to[te];
++k;
if(v==fa)continue;
dp[u][1][0]=min(pre[k-1][1][0]+pos[k+1][1][1],pre[k-1][1][1]+pos[k+1][1][0]);
dp[u][0][0]=min(pre[k-1][0][0]+pos[k+1][0][1],pre[k-1][0][1]+pos[k+1][0][0]);
dp[u][1][1]=pre[k-1][1][1]+pos[k+1][1][1];
dp[u][0][1]=pre[k-1][0][1]+pos[k+1][0][1];
dfs2(v,u);
}
}

int init(){
n=read();
for(int i=1,u,v;i<n;i++){
u=read(),v=read();
nex[++edge]=head[u];
to[head[u]=edge]=v;
nex[++edge]=head[v];
to[head[v]=edge]=u;
}
dfs(1,0);
dfs2(1,0);
printf("%lld",ans);
return -1;
}

int Score700points = init();

signed main(){
return 0;
}

NOI 2012 迷失游乐园

表面上看是一道期望 dp ,但转移似乎并不复杂。
总的来说 \(\displaystyle EX_u={1\over |son[u]|}\sum_{v\in son[u]}EX_v+1\) (1放里外都一样)

如果只是树,如此换根 dp 即可,但是 \(n=m\) 时是基环树,就需要特殊处理:

  1. 找环(无向连通图上)
  2. 对环上每个节点的子树做一次 dfs
  3. 求环上节点的答案,进而对其子树做第二次 dfs

思路很明晰,但细节处理较复杂

Code
#include <bits/stdc++.h>
#define int long long

using namespace std;

int read(){
    int i;
    scanf("%lld",&i);
    return i;
}

int n,m;
int head[100010],to[200010],nex[200010],we[200010],edge=0;
long double dp[100010];
int son[100010];
long double ans=0;
int dic[100010];
int ma[100010];
vector<int> cy;
long double pre[50],pos[50];
int li[50],ri[50];

int cn=0;

void dfst(int u,int fa){
    for(int te=head[u],v;te;te=nex[te]){
        v=to[te];
        if(v==fa)continue;
        dfst(v,u);
        son[u]++;
        dp[u]+=dp[v]+we[te];
    }
    if(son[u])dp[u]/=son[u];
    // printf("%lld %Lf\n",u,dp[u]);
}

void dfst2(int u,int fa){
    long double sum=0;
    for(int te=head[u],v;te;te=nex[te]){
        v=to[te];
        sum+=dp[v]+we[te];
        // printf("%lld %lld %Lf %lld\n",u,v,dp[v],we[te]);
    }
    //printf("%lld %lf %lld %lf\n",u,sum,son[u],sum/son[u]);
    ans+=sum/son[u];
    cn+=son[u];
    // printf("%lld %Lf %lld\n",u,sum,son[u]);
    for(int te=head[u],v;te;te=nex[te]){
        v=to[te];
        if(v==fa)continue;
        if(son[u]>1)dp[u]=(sum-dp[v]-we[te])/(son[u]-1);
        else dp[u]=0;
        son[v]++;
        dfst2(v,u);
    }
}

void tree(){
    dfst(1,0);
    dfst2(1,0);
}

int fc(int u,int fa){
    ma[u]=1;
    for(int te=head[u],v;te;te=nex[te]){
        v=to[te];
        if(v==fa)continue;
        if(ma[v]==1){
            cy.push_back(u);
            return v;
        }else if(ma[v]==2){
            while(ma[v]==2)cy.insert(++cy.begin(),v);
            cy.push_back(u);
            return v;
        }else{
            int tmp=fc(v,u);
            if(tmp){
                if(tmp!=-1)cy.push_back(u);
                if(tmp==u)return -1;
                else return tmp;
            }
        }
    }
    return 0;
}

void pseudotree(){
    cy.emplace_back(0);
    fc(1,0);
    int cl=cy.size()-1;
    for(int i=1;i<=cl;i++)cy.emplace_back(cy[i]),dic[cy[i]]=1;
    for(int i=1,u;i<=cl;i++){
        u=cy[i];
        for(int te=head[u],v;te;te=nex[te]){
            v=to[te];
            son[u]++;
            if(dic[v]){
                if(cy[i+cl-1]==v)li[i]=li[i+cl]=we[te];
                else ri[i]=ri[i+cl]=we[te];
                continue;
            }
            dfst(v,u);
            dp[u]+=dp[v]+we[te];
        }
    }
    for(int i=1,u;i<=cl;i++){
        u=cy[i];
        pre[i+1]=(son[cy[i+1]]-2)?dp[cy[i+1]]/(son[cy[i+1]]-2):dp[cy[i+1]];
        pos[i+cl-1]=(son[cy[i+cl-1]]-2)?dp[cy[i+cl-1]]/(son[cy[i+cl-1]]-2):dp[cy[i+cl-1]];
        // printf("%Lf %Lf ",pre[i+1],pos[i+cl-1]);
        for(int j=2;j<cl;j++)pre[i+j]=(pre[i+j-1]+dp[cy[i+j]]+li[i+j])/(son[cy[i+j]]-1);
        for(int j=cl-2;j>0;j--)pos[i+j]=(pos[i+j+1]+dp[cy[i+j]]+ri[i+j])/(son[cy[i+j]]-1);
        ans+=(pre[i+cl-1]+pos[i+1]+dp[u]+li[i]+ri[i])/son[u];
        double tmp=dp[u];
        cn+=son[u];
        printf("%lld %Lf %Lf %lld %Lf %Lf ",u,pre[i+cl-1],pos[i+1],son[u],dp[u],ans);
        for(int te=head[u],v;te;te=nex[te]){
            v=to[te];
            if(dic[v])continue;
            dp[u]=(pre[i+cl-1]+pos[i+1]+li[i]+ri[i]+tmp-dp[v]-we[te])/(son[u]-1);
            son[v]++;
            dfst2(v,u);
        }
        dp[u]=tmp;
        printf("%Lf\n",ans);
    }
    printf("%lld\n",cl);
    // printf("%lld\n",cn);
    if(cn!=2*n)throw runtime_error("dfsd");
}

signed main(){
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    n=read(),m=read();
    for(int i=1,u,v,w;i<=m;i++){
        u=read(),v=read(),w=read();
        nex[++edge]=head[u];
        to[head[u]=edge]=v,we[edge]=w;
        nex[++edge]=head[v];
        to[head[v]=edge]=u,we[edge]=w;
    }
    if(n==m)pseudotree();
    else tree();
    printf("%Lf",ans/n);
    return 0;
}


「COCI 2020.11」Svjetlo

抽象的朴素树形 dp。

这是一个树上的路径序列。因为是路径,所以可以就端点处理。

不妨设 :

  • \(dp[i][j][k]\)\(i\) 子树中使除 \(i\) 外所有节点均打开的最短路径长度,\(j=0/1/2\) 代表 \(i\) 子树中路径 没有端点/有一个端点/有两个端点,\(k=0/1\) 表示经过此路径后 \(i\) 的明灭情况。
  • \(sta[i]\)\(i\) 初始明灭情况。

那么,对于每个节点 \(i\) ,其初始 \(dp[i][0][sta[i] \oplus 1]=1\) ,其余为 \(+\infin\) 。(在其子树节点均打开的情况下,无需经过此子树,但 \(dp\) 数组描述的是子树被经过的情况,因而不能令 \(dp[i][0][sta[i]]=0\)

基本的转移有:

\[dp[u][j][k]=dp[u][j][k \oplus 1]+dp[v][0][1]+1(\text{u序列与v序列连接})\\ dp[u][j][k]=dp[u][j][k]+dp[v][0][0]+3 (\text{在原序列后缀-v u})\\ dp[u][j][k]=dp[u][j-1][k]+dp[v][1][1](j \ge 1)(\text{u序列与v序列连接})\\ dp[u][j][k]=dp[u][j-1][k]+dp[v][1][0]+2(j\ge 1,\text{在原序列后缀-v u})\\ dp[u][j][k]=dp[u][j-2][k]+dp[v][2][0]+1(j=2)(\text{v序列向u延伸})\\ dp[u][j][k]=dp[u][j-2][k]+dp[v][2][1]+3(j=2,\text{在原序列中插入-v u-})\\ \]

:对于 \(dp[v][2][1]\) ,若其余节点均打开,应作为整体的路径,因而需特殊转移。

Code
#include <bits/stdc++.h>
#define int long long
using namespace std;

int read(){
    int i;
    scanf("%lld",&i);
    return i;
}

int n;
int head[500010],to[1000010],nex[1000010],edge=0;
bool sta[500010];
int dp[500010][3][2];


int dfs(int u,int fa){
    dp[u][0][sta[u]]=2147483647;
    dp[u][0][sta[u]^1]=1;
    dp[u][1][0]=2147483647;
    dp[u][1][1]=2147483647;
    dp[u][2][0]=2147483647;
    dp[u][2][1]=2147483647;
    int mps=2147483647;
    for(int te=head[u],v;te;te=nex[te]){
        v=to[te];
        if(v==fa)continue;
        int mp=dfs(v,u);
        if(dp[v][0][0]==1)continue;
        if(mps<2147483647)mps=0;
        tie(dp[u][0][0],dp[u][0][1],dp[u][1][0],dp[u][1][1],dp[u][2][0],dp[u][2][1])=make_tuple(
        min({dp[u][0][1]+dp[v][0][1]+1,dp[u][0][0]+dp[v][0][0]+3}),
        min({dp[u][0][0]+dp[v][0][1]+1,dp[u][0][1]+dp[v][0][0]+3}),
        min({dp[u][0][1]+dp[v][0][0]+2,dp[u][0][0]+dp[v][1][1],dp[u][0][1]+dp[v][1][0]+2,dp[u][1][1]+dp[v][0][1]+1,dp[u][1][0]+dp[v][0][0]+3}),
        min({dp[u][0][0]+dp[v][0][0]+2,dp[u][0][1]+dp[v][1][1],dp[u][0][0]+dp[v][1][0]+2,dp[u][1][0]+dp[v][0][1]+1,dp[u][1][1]+dp[v][0][0]+3}),
        min({dp[u][0][0]+dp[v][1][0]+1,dp[u][1][0]+dp[v][0][0]+1,dp[u][1][0]+dp[v][1][1],dp[u][1][1]+dp[v][1][0]+2,dp[u][2][1]+dp[v][0][1]+1,dp[u][2][0]+dp[v][0][0]+3,dp[u][0][0]+dp[v][2][0]+1,dp[u][0][1]+dp[v][2][1]+3}),
        min({dp[u][0][1]+dp[v][1][0]+1,dp[u][1][1]+dp[v][0][0]+1,dp[u][1][1]+dp[v][1][1],dp[u][1][0]+dp[v][1][0]+2,dp[u][2][0]+dp[v][0][1]+1,dp[u][2][1]+dp[v][0][0]+3,dp[u][0][1]+dp[v][2][0]+1,dp[u][0][0]+dp[v][2][1]+3}));
        if(mp&&mps)mps=mp;
    }
    dp[u][1][0]=min(dp[u][1][0],dp[u][0][0]);
    dp[u][1][1]=min(dp[u][1][1],dp[u][0][1]);
    dp[u][2][0]=min(dp[u][2][0],dp[u][1][0]);
    dp[u][2][1]=min(dp[u][2][1],dp[u][1][1]);
    // printf("%lld %lld %lld %lld %lld %lld %lld %lld\n",u,dp[u][0][0],dp[u][0][1],dp[u][1][0],dp[u][1][1],dp[u][2][0],dp[u][2][1],mps);
    if(mps&&sta[u])return mps;
    else return dp[u][2][1];
    
}

signed main(){
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);

    n=read();getchar();
    for(int i=1;i<=n;i++)sta[i]=(getchar()=='1');
    for(int i=1,u,v;i<n;i++){
        u=read(),v=read();
        nex[++edge]=head[u];
        to[head[u]=edge]=v;
        nex[++edge]=head[v];
        to[head[v]=edge]=u;
    }
    int mp=dfs(1,0);
    printf("%lld",min({dp[1][0][1],dp[1][1][1],dp[1][2][1],mp}));
    return 0;
}

End_