【题目描述】
给定一颗有方向的树,改变一条边方向的代价为 $1$,求使得从两个点出发能到达树上所有节点的最小代价。
【输入格式】
第一行一个正整数 $n$。
接下来 $n-1$ 行,每行两个正整数 $u,v$,表示 $u$ 向 $v$ 连一条有向边。
【输出格式】
一行一个数表示答案。
【样例】
样例输入
4
2 1
3 1
4 1
样例输出
1
【数据范围与提示】
对于 $100\%$ 的数据,$1leq nleq 10^6]$。
【题解】
本题有两种思路。
第一种,考虑到有两个起点,设 $f[i][1/2][0/1]$ 表示以 $i$ 为根的子树内已经有 $1/2$ 个出发点,$i$ 是否为出发点,直接转移即可。
第二种,考虑到两个起点必然将整棵树分成两部分,考虑断边,换根 $dp$ 即可。
【代码】
#include<bits/stdc++.h> const int maxn=1000000+10; const int inf=1000000000; int n,f[maxn][2][2],g[2][2]; std::vector<int> E[maxn],G[maxn]; inline int read ( void ) { int x=0;char ch;bool f=true; while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) f=false; for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48); return f ? x : -x ; } using std::min; inline void dfs ( int u,int fr ) { f[u][0][0]=f[u][1][0]=f[u][1][1]=inf; for ( int v:E[u] ) if ( v!=fr ) { dfs(v,u); for ( int i=0;i<2;i++ ) for ( int j=0;j<2;j++ ) g[i][j]=f[u][i][j]; f[u][0][0]=min(g[0][0]+f[v][0][1],g[0][1]+min(f[v][0][0],f[v][0][1])+1); f[u][0][1]=g[0][1]+f[v][0][1]; f[u][1][0]=min(min(g[0][0]+min(min(f[v][0][0],f[v][1][1]),f[v][0][1]+1),g[0][1]+min(f[v][1][0],f[v][1][1])+1),min(g[1][0]+f[v][0][1],g[1][1]+min(f[v][0][0],f[v][0][1])+1)); f[u][1][1]=min(g[1][1]+f[v][0][1],g[0][1]+min(f[v][1][1],f[v][0][0])); } for ( int v:G[u] ) if ( v!=fr ) { dfs(v,u); for ( int i=0;i<2;i++ ) for ( int j=0;j<2;j++ ) g[i][j]=f[u][i][j]; f[u][0][0]=min(g[0][0]+f[v][0][1]+1,g[0][1]+min(f[v][0][0],f[v][0][1])); f[u][0][1]=g[0][1]+f[v][0][1]+1; f[u][1][0]=min(min(g[0][0]+min(min(f[v][0][0],f[v][1][1]+1),f[v][0][1]+0),g[0][1]+min(f[v][1][0],f[v][1][1])+0),min(g[1][0]+f[v][0][1]+1,g[1][1]+min(f[v][0][0],f[v][0][1]))); f[u][1][1]=min(g[1][1]+f[v][0][1],g[0][1]+min(f[v][1][1],f[v][0][0]))+1; } } signed main() { n=read(); for ( int i=1,u,v;i<n;i++ ) u=read(),v=read(),E[u].push_back(v),G[v].push_back(u); dfs(1,0); return !printf("%d ",min(min(f[1][0][0],f[1][0][1]),min(f[1][1][0],f[1][1][1]))); }