1. 程式人生 > 遊戲資訊 >爐石傳說:中速卡組的好朋友!新卡劍聖使用指南!

爐石傳說:中速卡組的好朋友!新卡劍聖使用指南!

Description

給一棵m個結點的無根樹,你可以選擇一個度數大於1的結點作為根,然後給一些結點(根、內部結點和葉子均可)著以黑色或白色。你的著色方案應該保證根結點到每個葉子的簡單路徑上都至少包含一個有色結點(哪怕是這個葉子本身)。

對於每個葉結點u,定義 \(c[u]\) 為從 \(u\) 到根結點的簡單路徑上第一個有色結點的顏色。給出每個 \(c[u]\) 的值,設計著色方案,使得著色結點的個數儘量少。

Solution

先給出一個結論:對於任意一個可行根,對答案沒有影響。

  • 證明:設當前根為 \(x\),有一與其相連的 \(y\) 。若 \(x\)\(y\) 都有顏色,則可以得出 \(x\)
    \(y\) 顏色必然不同(否則就不是最優解),因此根從 \(x\)\(y\) 不會對答案有影響。

那麼根據這個結論,不妨隨便找一個根。

考慮樹形 \(dp\)。設 \(f_{i,0/1/2}\) 表示當前節點 \(i\) 塗 0,1 或者不塗。

先考慮有顏色的。轉移分為葉子結點和非葉節點。

對於 \(f_{i,0}\),如果 \(son\) 是葉子節點並且要 1 ,那麼 \(f_{i,0}+1\)。如果是非葉節點,\(f_{i,0}=\sum\min(\min(f_{son,0}-1,f_{son,1}),f_{son,2})\)

對於 \(f_{i,1}\) 同理。

而對於 \(f_{i,2}\)

則沒有那麼多顧及,\(f_{i,2}=\sum\min(\min(f_{son,0},f_{son,1}),f_{son,2})\)

初值:對於葉子結點,\(f_{i,c_i}=1,f_{i,1-c_i}=f_{i,2}=\inf\),對於非葉節點 \(f_{i,0/1}=1\)

Code

#include<cstdio>
#include<algorithm>
#define N 100005
#define inf 123456789
using namespace std;
struct node
{
    int to,next,head;
}a[N<<1];
int n,m,rt,x,y,tot,c[N],f[N][3];
void add(int x,int y) {a[++tot].to=y;a[tot].next=a[x].head;a[x].head=tot;}
void dfs(int x,int fa)
{
    if (x>m) f[x][0]=f[x][1]=1;
    for (int i=a[x].head;i;i=a[i].next)
    {
        int y=a[i].to;
        if (y==fa) continue;
        dfs(y,x);
        if (y<=m) {if (c[y]==1) f[x][0]++;}
        else f[x][0]+=min(min(f[y][0]-1,f[y][1]),f[y][2]);
        if (y<=m) {if (c[y]==0) f[x][1]++;}
        else f[x][1]+=min(min(f[y][1]-1,f[y][0]),f[y][2]);
        f[x][2]+=min(min(f[y][0],f[y][1]),f[y][2]);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;++i)
        scanf("%d",&c[i]),f[i][c[i]]=1,f[i][c[i]^1]=f[i][2]=inf;
    rt=m+1;
    for (int i=1;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(rt,0);
    printf("%d\n",min(min(f[rt][0],f[rt][1]),f[rt][2]));
    return 0;
}