3124: [Sdoi2013]直径

3124: [Sdoi2013]直径

https://www.lydsy.com/JudgeOnline/problem.php?id=3124

分析:

  所有直径都经过的边,一定都是连续的一段。(画个图,反证一下)

  然后可以求出一条直径后,可以对每个点求出不经过直径到达的最远的距离。

  然后判断一下,找到左边分叉的最后一个,右边分叉的第一个,中间的点就是所有直径都经过的点。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4
 5 inline int read() {
 6     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch==‘-‘)f=-1;
 7     for (;isdigit(ch);ch=getchar())x=x*10+ch-‘0‘;return x*f;
 8 }
 9
10 const int N = 200100;
11
12 int head[N], q[N], fa[N], nxt[N<<1], to[N<<1], Enum;
13 LL dis[N], dist[N], val[N<<1], Len = 0, Mx = 0;
14 int Root, Qd, Zd, tot;
15 bool vis[N];
16
17 inline void add_edge(int u,int v,int w) {
18     ++Enum; to[Enum] = v; val[Enum] = w; nxt[Enum] = head[u]; head[u] = Enum;
19     ++Enum; to[Enum] = u; val[Enum] = w; nxt[Enum] = head[v]; head[v] = Enum;
20 }
21
22 void dfs1(int u,int pa) {
23     fa[u] = pa;
24     if (dis[u] > Len) {
25         Len = dis[u]; Root = u;
26     }
27     for (int i=head[u]; i; i=nxt[i]) {
28         int v = to[i];
29         if (v == pa) continue;
30         dis[v] = dis[u] + val[i];
31         dfs1(v, u);
32     }
33 }
34
35 void dfs2(int u,int pa) {
36     vis[u] = true;
37     if (dist[u] > Mx) {
38         Mx = dist[u]; Root = u;
39     }
40     for (int i=head[u]; i; i=nxt[i]) {
41         int v = to[i];
42         if (v == pa || vis[v]) continue;
43         dist[v] = dist[u] + val[i];
44         dfs2(v, u);
45     }
46 }
47
48 int main() {
49
50     int n = read();
51     for (int i=1; i<n; ++i) {
52         int u = read(), v = read(), w = read();
53         add_edge(u, v, w);
54     }
55     dfs1(1, 0);
56     Len = 0; Qd = Root; dis[Qd] = 0;
57     dfs1(Root, 0);
58     Zd = Root;
59
60     for (int i=Zd; i; i=fa[i]) q[++tot] = i, vis[i] = true;
61
62     int L = tot, R = 1;
63     for (int i=tot; i>=1; --i) {
64         Mx = 0;dfs2(q[i], 0);
65         if (!Mx) continue;
66         if (Mx == dis[q[i]]) L = i; // 保证所有直径都经过,左边分叉的最后一个
67         if (Len - dis[q[i]] == Mx) {R = i; break;} // 右边分叉的第一个
68     }
69     cout << Len << "\n" << L - R;
70     return 0;
71 }

原文地址:https://www.cnblogs.com/mjtcn/p/9350302.html

时间: 2024-11-09 09:15:09

3124: [Sdoi2013]直径的相关文章

Bzoj 3124: [Sdoi2013]直径 题解

3124: [Sdoi2013]直径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1222  Solved: 580[Submit][Status][Discuss] Description 小Q最近学习了一些图论知识.根据课本,有如下定义.树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度.如果一棵树有N个节点,可以证明其有且仅有N-1 条边. 路径:一棵树上,任意两个节点之间最多有一条简单路径.我们用 dis(a,b)表示点a和点

BZOJ 3124 SDOI2013 直径

首先直径是很好求的,先以任意点为根DFS求出最远点,再以最远点为根求出第二个点 两个点之间的距离即为直径 显然对于第二问,答案是直径上的某一段,且满足不可向左右扩展出跟直径等长的路径 那么我们就可以暴力枚举直径上的点,看看它是否可以向右和向左扩展即可 #include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> #include<iostream> using n

[Sdoi2013] 直径

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

[树的直径] 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

[Sdoi2013]直径(树的直径)

//36分 #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<algorithm> #include<map> using namespace std; typedef long long ll; #define setfire(name) freopen(#name".in","r",st

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

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

【Luogu P3304】[SDOI2013]直径

题目链接 题意,求一棵树被所有直径经过的边的条数. 这题是我们8.25KS图论的最后一题,当时我果断打了暴力求所有直径然后树上差分统计的方法,好像有点小问题,boom0了. 考完改这题,改了好久,各种各样的小bug,至少有七八个... 思路:先随便找一条直径,然后从一个端点开始遍历这条直径,如果当前点能分叉出一条直径,那么这条直径后面的点都不可能被所有直径穿过了,于是我们找到第一个能分叉的点,然后再从这个点往回遍历,找到第一个能分叉的点,这2个点中间的路径的边即为所求. 代码就算了吧,打这题时我

P3304 [SDOI2013]直径(【模板】树直径的必经边)

题目地址 基本思路: 题目要求树直径的必经边,那么首先应当获取一条直径. 获取直径后从直径上的两个端点分别遍历一次直径,每次遍历直径时从直径上的每个点分别dfs一次并不经过直径上的点,如果深度可以被替换则说明非必经边. #include<cstdio> #include<iostream> #include<queue> #include<cstring> #define ll long long using namespace std; const int