|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 柿子饼同学 于 2024-6-22 19:30 编辑
树网的核 link
1 遍过
4 h

知识点
单调队列
思路
直径的性质: 到一个点的最远距离的点在直径上, 偏心距只会在直径两个端点和不在直径上的路径上产生答案
###1
先找出直径, 记下来, 对于每个直径上的点求出这个点不过直径所能走到最远的点的距离, 记作 val
然后在直径上跑单调队列, 对于一条路径 , 它的偏心距就是 max(路径中val的最大值, 直径两端点到路径端点的最大值)
###2
可以简单一些, 只求出 max(一个点不过直径的最远距离)
根据直径的性质可知 , 当路径不包含这个最大值的点时, 路径端点到直径端点的答案更大
当包含时, 可以让路径端点到直径端点和这个取 max 即可
O(n)
需要进步的地方
代码太长, 加了一些不必要的数组, 要精简代码
一开始写的时候没想清楚, 浪费了时间
整体想不出来, 可以想想单个点是怎么算答案的, 对思考有帮助
先想想一个操作影响的范围, 不要一直想着 memset 0
II 方法很好, 求直径之后沿着 f 数组跳上去即可, 不用截下来或打标记
写 II 的时候 dfs 忘记递归了...
最终代码
- #include <bits/stdc++.h>
- using namespace std;
- const int N = 5e5 + 5;
- typedef long long ll;
- struct Edge{
- int v, w;
- };
- int n;
- ll s;
- vector<Edge> e[N];
- // f 父节点, d 到根的距离
- int p, q, f[N];
- ll d[N];
- void dfs1(int u, int pa, int & id){
- for(auto ed : e[u]){
- if(ed.v == pa) continue;
- f[ed.v] = u;
- d[ed.v] = d[u] + ed.w;
- if(d[ed.v] > d[0]){
- d[0] = d[ed.v];
- id = ed.v;
- }
- dfs1(ed.v, u, id);
- }
- }
- void Work(){
- f[1] = 0, d[1] = 0;
- dfs1(1, 0, p); // 直径一端点是 p
- f[p] = 0, d[p] = 0, d[0] = 0;
- dfs1(p, 0, q); // 直径顺序: p -> q
- // 不需要 memset, 后续计算不会算到直径
- }
- // 求不过直径的最大距离
- ll mxd = 0;
- void dfs2(int u, int f){
- for(auto ed : e[u]){
- if(ed.v == f) continue;
- d[ed.v] = d[u] + ed.w;
- if(d[ed.v] > mxd) mxd = d[ed.v];
- dfs2(ed.v, u);
- }
- }
- int main(){
- ios::sync_with_stdio(0);
- cin.tie(0);
- cin >> n >> s;
- for(int i = 1; i < n; i++){
- int x, y, z;
- cin >> x >> y >> z;
- e[x].push_back({y, z});
- e[y].push_back({x, z});
- }
- Work();
-
- // 求 mxd
- // j 是 i 的上一个节点
- for(int i = q, j = 0; i; j = i, i = f[i]){
- for(auto ed : e[i]){
- if(ed.v == f[i] || ed.v == j) continue;
- // 现在只有不在直径上的点了
- d[ed.v] = ed.w;
- mxd = max(mxd, d[ed.v]);
- dfs2(ed.v, i);
- }
- }
- // 尺取路径 [i, j]
- ll ans = 2e18;
- for(int i = q, j = q; i; i = f[i]){
- while(f[j] != f[i] && d[j] - d[i] > s) j = f[j];
- ans = min(ans, max({mxd, d[i], d[q] - d[j]}));
- }
- cout << ans;
- return 0;
- }
复制代码 一开始的代码
- /*
- 找直径, 标记, 取下直径
- 每个点求一个不在直径上的侧边最远点距离
- 每个点的权值 max(最远点距, 直径两端点到点的距离)
- 单调队列跑直径, 求最大值最小
- */
- #include <bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const int N = 5e5 + 5;
- struct Edge{
- int v, w;
- };
- vector<Edge> e[N];
- int n;
- ll m;
- // 找最远点
- bitset<N> vis;
- int f[N];
- ll maxd, id;
- ll dist[N];
- int d[N], cnt;
- void dfs(int u, int pa, ll dis){
- f[u] = pa;
- if(dis > maxd){
- maxd = dis;
- id = u;
- }
- for(auto ed : e[u]){
- if(ed.v == pa || vis[ed.v]) continue;
- dfs(ed.v, u, dis + ed.w);
- }
- }
- // 求直径, 扒下来
- void Getd(){
- dfs(1, 0, 0);
- memset(f, 0, sizeof(f));
- maxd = 0;
- int rt = id;
- dfs(rt, 0, 0);
- maxd = 0;
-
- while(id != rt){
- d[++cnt] = id;
- vis[id] = 1;
- for(auto ed : e[id]){
- if(ed.v == f[id]){
- dist[cnt] = ed.w;
- break;
- }
- }
- id = f[id];
- }
- vis[rt] = 1;
- d[++cnt] = rt;
- for(int i = cnt - 1; i >= 1; i--) dist[i] += dist[i + 1];
- }
- // 求权值
- ll val[N];
- void Getval(){
- for(int i = 1; i <= cnt; i++){
- dfs(d[i], f[d[i]], 0);
- val[i] = maxd;
- maxd = 0;
- }
- }
- int q[N], l = 1, r = 0;
- int main(){
- ios::sync_with_stdio(0);
- cin.tie(0);
- cin >> n >> m;
- for(int i = 1; i < n; i++){
- int x, y, z;
- cin >> x >> y >> z;
- e[x].push_back({y, z});
- e[y].push_back({x, z});
- }
- Getd();
- Getval();
- int last = 1;
- ll ans = LONG_LONG_MAX;
- for(int i = 1; i <= cnt; i++){
- while(r >= l && val[q[r]] <= val[i]) r--;
- while(last <= i && dist[last] - dist[i] > m) last++;
- q[++r] = i;
- while(l <= r && q[l] < last) l++;
- ans = min(ans, max({dist[1] - dist[last], dist[i], val[q[l]]}));
- }
- cout << ans;
- return 0;
- }
复制代码
|
评分
-
查看全部评分
|