LXIII.CF1029E Tree with Small Distances
我们发现,如果一个点与\(1\)连了边,那么它的儿子们以及它的父亲都会变成合法的。
因此我们可以设\(f[i][0/1/2]\)表示:\(i\)的某个儿子中有边/\(i\)自己有边/\(i\)的父亲应该有边的最小值。
转移:
\(0\):可以从儿子的\(0\)或\(1\)转移,且儿子中至少有一个为\(1\)(即,找到\(1\)与\(0\)差最小的那个换成\(1\))
\(1\):\(0/1/2\)皆可,取\(\min\)即可。
\(2\):\(0/1\)取\(\min\)。
复杂度\(O(n)\)。
最后说一下答案,应该是\(1\)的所有儿子的\((f[x][1]-1)\)的和,因为\(1\)的所有儿子都相当于连了一条免费的边。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,head[1001000],cnt,f[1001000][3],res;//0:have a son;1:itself;2:have a father
struct node{
int to,next;
}edge[2001000];
void ae(int u,int v){
edge[cnt].next=head[u],edge[cnt].to=v,head[u]=cnt++;
}
void dfs(int x,int fa){
int mn=0x3f3f3f3f;
f[x][1]=1;
for(int i=head[x],y;i!=-1;i=edge[i].next){
if((y=edge[i].to)==fa)continue;
dfs(y,x);
f[x][0]+=min(f[y][0],f[y][1]),mn=min(mn,f[y][1]-f[y][0]);
f[x][1]+=min(f[y][0],min(f[y][1],f[y][2]));
f[x][2]+=min(f[y][0],f[y][1]);
if(x==1)res+=f[y][1]-1;
}
f[x][0]+=max(mn,0);
}
int main(){
scanf("%d",&n),memset(head,-1,sizeof(head));
for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),ae(x,y),ae(y,x);
dfs(1,0);
printf("%d\n",res);
return 0;
}