题目大意:给一棵树,求加k条边之后,从1号点遍历每个点之后再回到1号点的最小距离和。k=1或2
题解: 对于1棵树,遍历所有点需要走2∗(n−1)次,ans=2∗(n−1)
k=1,求出树上最长链,把最长链首尾相连, ans=ans−len+1
k=2,将第一次选取的最长链边权值置为-1,再重复操作1
如果两次走到重复的边呢?第一次算这条边的时候加了1,第二次的时候加的是-1,这条边没有贡献,相当于是把两条交错的链变成了两条分开的链,Orz
因为有负边权,这题没法用bfs/dfs找直径,需要树形dp
我的收获:神奇的消除重复影响的方法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=100005;
int n,t,ans,c,k;
int rt,tia;
int head[M],dis[M],fa[M],s1[M],s2[M];
struct edge{int to,val,nex;}e[M*4];
void add(int u,int v){e[t]=(edge){v,1,head[u]};head[u]=t++;}
int dfs(int x,int fa)
{
int fr=0,se=0;
for(int i=head[x];i!=-1;i=e[i].nex){
int v=e[i].to;
if(v==fa) continue;
int l=e[i].val+dfs(v,x);
if(l>fr) se=fr,fr=l,s2[x]=s1[x],s1[x]=i;
else if(l>se) se=l,s2[x]=i;
}
if(fr+se>tia) tia=fr+se,rt=x;
return fr;
}
void work()
{
dfs(1,0);
ans=2*(n-1)-tia+1;
if(k==1) {printf("%d\n",ans);return ;}
tia=0;
for(int i=s1[rt];i;i=s1[e[i].to]) e[i].val=e[i^1].val=-1;
for(int i=s2[rt];i;i=s1[e[i].to]) e[i].val=e[i^1].val=-1;
dfs(1,0);ans=ans-tia+1;
cout<<ans<<endl;
}
void init()
{
int x,y;
cin>>n>>k;t=0;memset(head,-1,sizeof(head));
for(int i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
}
int main()
{
init();
work();
return 0;
}