#include <stdio.h>
int main()
{
puts("转载请注明出处[vmurder]谢谢");
puts("网址:blog.csdn.net/vmurder/article/details/45062689");
}
对于 k==0 的情况:
我们发现遍历一棵树最后回到原点,那么对于所有的边,我们都是走过去,再走回来。
答案 (n−1<<1)
对于 k==1 的情况
设每条边长度为1,然后树上找最长链,然后这条链走过去就不再一步步往回了,直接从链底连一条边去链顶,然后链中间连的那些点,直接走过去再走回来,它们那些边的答案是不变的。
答案 (n−1<<1)−(链长度)+1
可以证明不能减得更多啦。
z对于 k==2 的情况
设上一次的直径上的边的边长都为-1,因为再走一次会坑,然后树上找最长链。
答案 (n−1<<1)−(链1长度)+1−(链2长度)+1
可以证明不能减得更多啦。
我在这里就不贴出证明了,以免给读者养成不自己证明的惰性(其实是我这两天惰性有点大2333)
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define inf 0x3f3f3f3f
using namespace std;
struct Eli
{
int v,len,next;
}e[N<<1];
int head[N],cnt;
inline void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].len=1;
e[cnt].next=head[u];
head[u]=cnt;
}
int fa[N],pre[N];
int f1[N],f2[N],s1[N],s2[N];
int point;
bool cmp(int a,int b)
{
int x=f1[a]+f2[a];
int y=f1[b]+f2[b];
return x>y;
}
void dfs(int x,int p)
{
int i,u,v;
s1[x]=s2[x]=x;
for(i=head[x];i;i=e[i].next)
{
v=e[i].v;
if(v==p)continue;
fa[v]=x,pre[v]=i;
dfs(v,x);
if(f1[v]+e[i].len>f1[x])
{
f2[x]=f1[x];
s2[x]=s1[x];
f1[x]=f1[v]+e[i].len;
s1[x]=s1[v];
}
else if(f1[v]+e[i].len>f2[x])
{
f2[x]=f1[v]+e[i].len;
s2[x]=s1[v];
}
}
if(cmp(x,point))point=x;
}
int n,m,p1,p2,x1,x2;
int main()
{
// freopen("test.in","r",stdin);
int i,j,k;
int a,b,c;
int ans;
scanf("%d%d",&n,&m),ans=n-1<<1;
for(cnt=i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
f1[0]=f2[0]=-inf;
dfs(1,0);
ans-=f1[point]+f2[point]-1;
if(m==1)printf("%d\n",ans);
else {
memset(f1,0,sizeof f1);
memset(f2,0,sizeof f2);
for(a=s1[point];a!=point;a=fa[a])e[pre[a]].len=-1,e[pre[a]^1].len=-1;
for(a=s2[point];a!=point;a=fa[a])e[pre[a]].len=-1,e[pre[a]^1].len=-1;
dfs(1,0);
ans-=f1[point]+f2[point]-1;
printf("%d\n",ans);
}
return 0;
}
这里提供一下代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
using namespace std;
struct Eli
{
int v,len,next;
}e[N<<1];
int head[N],cnt;
inline void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].len=1;
e[cnt].next=head[u];
head[u]=cnt;
}
queue<int>q;
int f[N],pre[N],fa[N];
bool vis[N];
int bfs(int root)
{
int i,u,v,ans,x=-1;
memset(vis,0,sizeof vis);
q.push(root),f[root]=0,vis[root]=1;
while(!q.empty())
{
u=q.front(),q.pop();
if(x<f[u])x=f[u],ans=u;
for(i=head[u];i;i=e[i].next)
{
if(!vis[v=e[i].v])
{
f[v]=f[u]+e[i].len;
vis[v]=1;
fa[v]=u;
pre[v]=i;
q.push(v);
}
}
}
return ans;
}
int n,m,p1,p2,x1,x2,root;
int main()
{
freopen("test.in","r",stdin);
int i,j,k;
int a,b,c;
scanf("%d%d",&n,&m);
for(cnt=i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
root=bfs(1),p1=bfs(root),x1=f[p1];
if(m==1)printf("%d\n",(n-1)*2-x1+1);
else {
while(p1!=root)e[pre[p1]].len=-1,e[pre[p1]^1].len=-1,p1=fa[p1];
root=bfs(1),p2=bfs(root),x2=f[p2];
printf("%d\n",(n-1)*2-x1+1-x2+1);
}
return 0;
}
/* 反例: 5 2 1 2 1 3 3 4 3 5 错误原因: 两遍bfs时有多个点距离根距离相同,而最终答案却不同Qwq 总之你只要敢写我就敢对着代码hack。 */
我可没说它AC了啊