[SDOI2013]直径 (树的直径,贪心)

题目链接


Solution

我们直接找到一条直径 \(s\),起点为 \(begin\),终点为 \(end\).
从前往后遍历点 \(u\) ,若子树中最大的距离与 \(dis(u,begin)\) 相等.
很显然这个点不在公共线段上,很显然可以用子树的中的一段接上,形成一条新的直径.
然后从后往前遍历,同样的道理...
然后找到两个节点 \(l,r\) 然后答案即为 \(r-l\).
记得要开 \(longlong\).


Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=300008;
struct sj{
ll to,next,w;
}a[maxn*2];
ll head[maxn],size;
ll n,s,x,y,w;
ll read()
{
    ll f=1,w=0; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){w=w*10+ch-'0';ch=getchar();}
    return f*w;
}
void add(ll x,ll y,ll w)
{
    a[++size].to=y;
    a[size].next=head[x];
    head[x]=size;a[size].w=w;
}

ll last,num,now,cnt,v[maxn],bcnt;
ll ans[maxn],maxx,road[maxn];
ll dist[maxn],b[maxn],ansb[maxn];
ll sum[maxn];
void dfs(ll x)
{
    v[x]=1;
    road[++cnt]=x;
    for(ll i=head[x];i;i=a[i].next)
    {
         ll tt=a[i].to;
         if(!v[tt])
         {
            now+=a[i].w;
            b[++bcnt]=a[i].w;
            dfs(tt); cnt--;bcnt--;
            now-=a[i].w;
         }
    }
    if(now>maxx)
    {
        num=cnt; last=x; maxx=now;
        for(ll i=1;i<=cnt;i++)
        ans[i]=road[i],ansb[i]=b[i];
    }
}

void getdist(ll x)
{
    v[x]=1;
    maxx=max(maxx,now);
    for(ll i=head[x];i;i=a[i].next)
    {
        ll tt=a[i].to;
        if(!v[tt])
        {
            now+=a[i].w;
            getdist(tt);
            now-=a[i].w;
        }
    }
}

int main()
{
    n=read();
    for(ll i=1;i<n;i++)
    {
        x=read(); y=read(); w=read();
        add(x,y,w); add(y,x,w);
    }
    dfs(1);
    memset(v,0,sizeof(v));
    maxx=0; cnt=0; now=0;
    dfs(last);
    memset(v,0,sizeof(v));
    for(ll i=1;i<=num;i++)v[ans[i]]=1;
    for(ll i=2;i<=num;i++)sum[i]=sum[i-1]+ansb[i-1];
    for(ll i=1;i<=num;i++)
    {maxx=0,getdist(ans[i]),dist[i]=maxx;}

    ll l=1,r=num;
    for(int i=1;i<=num;i++)
    if(sum[i]==dist[i]) l=i;
    for(int i=num;i>=1;i--)
    if(sum[num]-sum[i]==dist[i]) r=i;
    cout<<sum[num]<<endl;
    cout<<r-l<<endl;

}

原文地址:https://www.cnblogs.com/Kv-Stalin/p/9508261.html

时间: 2024-10-09 16:42:45

[SDOI2013]直径 (树的直径,贪心)的相关文章

hdu 4612 缩点 桥 树的直径

// http://acm.hdu.edu.cn/showproblem.php?pid=4612 // 大致题意: 给n个点和m条边,组成一个无向连通图,问  给我加一条边的权力(可连接任意两点)->让图的桥数量最小,输出此时桥的数量.(2<=N<=200000, 1<=M<=1000000) // 无向环里面的边没有桥,缩点,因为是连通图,所以缩完点后构成了一棵树,每条树边都是一个桥.要加一条边使得加完后图的桥数最小,结合上述,所以选择连接树直径的两端点.ans = 原先

树的直径、树的重心与树的点分治

树的直径 树的直径(Diameter)是指树上的最长简单路. 直径的求法:两遍搜索 (BFS or DFS) 任选一点w为起点,对树进行搜索,找出离w最远的点u. 以u为起点,再进行搜索,找出离u最远的点v.则u到v的路径长度即为树的直径. 简单证明: 如果w在直径上,那么u一定是直径的一个端点.反证:若u不是端点,则从直径另一端点到w再到u的距离比直径更长,与假设矛盾. 如果w不在直径上,且w到其距最远点u的路径与直径一定有一交点c,那么由上一个证明可知,u是直径的一个端点. 如果w到最远点u

树的直径与树的重心

树的直径 树的直径是指树上的最长简单路. 直径的求法:两遍搜索 任选一点w为起点,对树进行搜索,找出离w最远的点u. 以u为起点,再进行搜索,找出离u最远的点v. 则u到v的路径长度即为树的直径. ---------------------------------------------------------------- 树的重心树的重心: 找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心. 删去重心后,生成的多棵树尽可能平衡. 树的重心可以通过简单的两次搜索求出,

[树的直径] SDOI2013 直径

SDOI2013 直径 题目描述 小Q最近学习了一些图论知识.根据课本,有如下定义.树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度.如果一棵树有N个节点,可以证明其有且仅有N-1 条边. 路径:一棵树上,任意两个节点之间最多有一条简单路径.我们用 dis(a,b)表示点a和点b的路径上各边长度之和.称dis(a,b)为a.b两个节点间的距离. 直径:一棵树上,最长的路径为树的直径.树的直径可能不是唯一的. 现在小Q想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有

SDOI2013 直径(树的直径必经边)

SDOI2013 直径 题目传送 sol: 先求出任一直径同时把直径拎出来,树的非直径部分全部挂在直径上(如下). 对于直径上的每一个点i,如果存在它到非直径上点的最大距离\(g[i]\)等于它到直径两端点中较短的那一段\(d[i]\), 则说明这一段也可以成为直径中的一部分. 而我们需要得到所有直径的交,画图可以发现假设两端(以中点为界)都存在上述的点,最逼近的两点间的边即为所求! 具体可以看代码实现. code: #include<bits/stdc++.h> #define IL inl

ZOJ 3820 Building Fire Stations 贪心+树的直径

不知道为什么是对的,但是举不出反例,比赛的时候队友提出找树的直径,不过我没敢写,真是可惜. 具体做法就是先找到原树的直径,然后删去最中间的一条边,变为两个子树,分别球两颗子树的直径中点,便是答案了. #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <stack> #include <map> #include <

树的直径求法与性质(附例题)

树的直径指树上距离最远的两点间的距离,它在树上问题上有许多应用,往往通过树的直径的性质可以将一个高时间复杂度的解法变为线性求解.对于树上两点间距离通常有三种定义,我们根据这三种情况分别讨论一下它的性质 树的直径的求法: 树的直径有两种求法,时间复杂度都是O(n) ①贪心求法: 贪心求直径的方法是任意找一个点为根,dfs整棵树找到距离他最远的点xx,再以这个点x为根求出距离它最远的点y,(x,y)即为直径 ②DP求法: DP求直径的方法是对于每个点记录这个点子树中的最长链及与最长链处于不同子树中的

poj 1985 Cow Marathon 【树的直径】

题目:poj 1985 Cow Marathon 题意:给出一个树,让你求树的直径. 分析: 树的直径:树上两点之间的最大距离. 我们从任意一点出发,BFS一个最远距离,然后从这个点出发,在BFS一个最远距离,就是树的直径. AC代码: /* POJ:1985 Cow Marathon 2014/10/12/21:18 Yougth*/ #include <cstdio> #include <iostream> #include <algorithm> #include

poj1849(求树的直径)

题目链接:http://poj.org/problem?id=1849 题意:有一颗n个结点的带权的无向树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 但是最后机器人不要求回到出发点. 问你两个机器人走的路总长之和的最小值是多少? 分析:如果从某点出发遍历完一棵树再回来,那么所有边都会走两遍,而从某点有两个机器人出发去遍历,因为不用回来,所以最后那两个人距离越远越好,可以从树的直径上某个点背道而驰,那么这段距离(树的直径)只走了一遍,其他的要走两遍,所以ans=sum*2-l