bzoj 3124 直径

很久没有写博客了。。中间有刷题哦!朕没有偷懒!
ps看我博客的改版没有。。【朕也是会写那一点点HTML的人哈哈哈】

这题损耗老朽身心无数。。。【【哀家不要打我】】

题意很清晰喽。
第一问,求树的直径 – 这个可以 两边BFS。
第二问,求所有树的直径必须经过的边。

分析:
观察 & 从 zxy 学长的博客中可以发现,任选一个点之后BFS最远距离的那个点。那个点必然是直径的一个端点。【【也可以用2遍dfs】】

主要是第二问,这道题我们可以 分析【瞎搞】出一个性质 如果直径上的点在不是直径的link上的最大距离到它的直径的一个端点的距离如果相等那它一定不是必经过的点。

意思就是
老朽字丑勿怪

如果是相等的,那它范围必然可以缩小。
比如:
同上

所以这道题就是可以写的。
辣么为什么我WA了?很多次?
第一,边的范围又一次按点开的。。。
第二,边的权值足以到 long long 没开 :)
第三,其中循环直径上的每个点 循环错了。你敢信?然而我就这么错了。。。

总结完毕。
代码一向比较长。。。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<iostream>
#define LL long long
using namespace std;
//by mars_ch
int n,cnt,ans,tot; 
struct Edge
{
    int s,t,nxt;
    LL v;
}e[400001];
LL md;
int first[200001],f[200001],p[2000001],vis[200001];
LL dis[200001],d[200001]; 
void add(int x,int y,LL z){e[tot].s=x;e[tot].t=y;e[tot].v=z;e[tot].nxt=first[x];first[x]=tot++;};
int bfs(int u)    //寻找最远的点 
{
    cnt=0;
    for(int i=1;i<=n;i++) dis[i]=-1;
    queue<int> q;
    q.push(u);
    dis[u]=0;
    LL maxd=0;
    int pos=u;
    while(!q.empty())
    {
        int tp=q.front();
        q.pop();
        if(dis[tp]>maxd)
        {
            maxd=dis[tp];
            pos=tp;
        }
        for(int i=first[tp];i!=-1;i=e[i].nxt)
        {
            if(dis[e[i].t] == -1)
            {
                dis[e[i].t]=dis[tp]+e[i].v;
                q.push(e[i].t);
                p[e[i].t]=tp;
            }
        }
    }
    return pos;
}
void find(int x,int fa)
{
    if(md<d[x]) md=d[x];
    for(int i=first[x];i!=-1;i=e[i].nxt)
    {
        if(e[i].t!=fa && vis[e[i].t]!=1)
        {
            d[e[i].t]=d[x]+e[i].v;
            find(e[i].t,x);
        }
    }
}
int main()
{   
    int a,b,left=0,right=0;
    LL c;
    scanf("%d",&n);
    memset(first,-1,sizeof(first));
    memset(vis,0,sizeof(vis));
    memset(p,0,sizeof(p));
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d%lld",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    int low=bfs(b);
    int high=bfs(low);       //起点终点 p【i】表示中间的点
    printf("%lld\n",dis[high]);
    for(int i=high;i!=low;i=p[i])vis[i]=1;
    vis[low]=1;
    int l=low;
    int r=high;
    bool flag=true;
    for(int i=high;i!=low;i=p[i])
    {
        LL ld=dis[i];
        LL rd=dis[high]-dis[i];
        md=0;
        find(i,0);
        if(md == ld && flag == 1) {l=i;flag=0;}
        if(md == rd) {r=i;}
    }
    for(int i=r;i!=l;i=p[i]) ans++;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值