【BZOJ1912】【Apio2010】巡逻 树上最长链(才不是树的直径呢)

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/45062689");
}

题解:

对于 k==0 的情况:
我们发现遍历一棵树最后回到原点,那么对于所有的边,我们都是走过去,再走回来。
答案 (n1<<1)

对于 k==1 的情况
设每条边长度为1,然后树上找最长链,然后这条链走过去就不再一步步往回了,直接从链底连一条边去链顶,然后链中间连的那些点,直接走过去再走回来,它们那些边的答案是不变的。
答案 (n1<<1)()+1
可以证明不能减得更多啦。

z对于 k==2 的情况
设上一次的直径上的边的边长都为-1,因为再走一次会坑,然后树上找最长链。
答案 (n1<<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;
}

不是说有一种bfs求树的直径的方法么?

这里提供一下代码:

#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了啊

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值