当不存在没有被染色的边时: 若被自己染黑,则由于规则 1,这个点染了以后所有儿子都会被染 色,因此儿子的情况可以随便选:
fu,1,0 = 1 + ∑min{fv,0/1/2,0/1}
若被儿子染黑,则必须要选择一个儿子传递上去,儿子能传递上 来只有两种情况: fu,0,0 = minv{min{fv,0,0, fv,1,0} + ∑ v ′̸=v min{fv,0,0, fv,0,1, fv,1,0}}
若只被父亲染黑,则儿子不能把 u 染黑,为了 u 和儿子之间的边 能够被覆盖,只能用规则 2,发现只有一种情况:fu,2,0 = ∑fv,0,1
当与儿子的边中存在一条没有被染色时: 若被儿子染黑,那么要在上面的 fu,0,0 中转移式的 v ′ 中再抽出一 个 v ′′,这个 v ′′ 的贡献只能是 min{fv ′′ ,2,0/1}
若只被父亲染黑,同理,在上面的 fu,2,0 中转移式的 v 中抽出一个 v ′,这个 v ′ 的贡献也只能是 min{fv ′ ,2,0/1}
时间复杂度 O(n)。
存在一条没有被染色的提取出来的代价的意义是,我们定义的状态f[u][2][1]是u与儿子在一条边没被染色,这条边因为规则4,其他边都被染色的情况下染色的状态,因此我们需要提取出这个信息。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=4e5+10; const int inf=0x3f3f3f3f; ll f[N][3][3]; int h[N],ne[N],e[N],idx; int in[N]; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void dfs(int u,int fa){ f[u][1][0]=1,f[u][2][0]=0; if(in[u]==1&&fa!=-1) return ; f[u][0][0]=f[u][0][1]=f[u][2][0]=f[u][2][1]=0; ll g[2]={0,inf}; ll tmp[2][2]={0,inf,inf,inf}; for(int i=h[u];i!=-1;i=ne[i]){ int v=e[i]; if(v==fa) continue; dfs(v,u); ll tmp1=min(f[v][1][0],min(f[v][0][1],f[v][0][0])); g[1]=min(g[1]+tmp1,g[0]+min(f[v][1][0],f[v][0][0])); g[0]+=tmp1; tmp[1][1]=min(tmp[1][1]+tmp1,min(tmp[0][1]+min(f[v][1][0],f[v][0][0]),tmp[1][0]+min(f[v][2][1],f[v][2][0]))); tmp[1][0]=min(tmp[1][0]+tmp1,tmp[0][0]+min(f[v][1][0],f[v][0][0])); tmp[0][1]=min(tmp[0][1]+tmp1,tmp[0][0]+min(f[v][2][1],f[v][2][0])); tmp[0][0]+=tmp1; f[u][1][0]+=min(f[v][1][0],min(f[v][0][0],min(f[v][0][1],min(f[v][2][0],f[v][2][1])))); f[u][2][1]=min(f[u][2][0]+min(f[v][2][1],f[v][2][0]),f[u][2][1]+f[v][0][1]); f[u][2][0]+=f[v][0][1]; } f[u][0][0]=g[1]; f[u][0][1]=tmp[1][1]; } int main(){ ios::sync_with_stdio(false); memset(h,-1,sizeof h); int n; cin>>n; for(int i=1;i<=n;i++){ for(int j=0;j<3;j++){ for(int k=0;k<2;k++) f[i][j][k]=0x3f3f3f3f; } } for(int i=1;i<n;i++){ int a,b; cin>>a>>b; add(a,b); add(b,a); in[a]++; in[b]++; } dfs(1,-1); cout<<min(f[1][1][0],min(f[1][0][0],f[1][0][1]))<<endl; }