NOI 2013 快餐店

NOI 线段树


题目传送点

说白了,就是给个N个点N条边的图,然后求一个点(不一定是给的那N个点),到所有给定的点的路程最大值最小

=≡Σ((( つ•̀ω•́)つ我是蒟蒻,想了好久的二分和三分(最大值最小嘛)


题解

[朴素的想法]

我们可以先假设:如果给的是树而不是图呢?
树就很简单,O(n)O(n)求出树的直径(不会点这里),因为要求最大值最小嘛,非常显然,直径的一半就是答案。
现在呢,给你一个图,怎么办,想这种树好做图不好做的可以把图尽量向树的方向靠拢,现在N个点,N条边,也就是说是一棵外向树(一个环,一些环的定点连着很多子树),那么我们就可以求出这个环(假设是KK条边的环),然后枚举这个环上哪一条边不走,然后再做KK次找树的直径即可。
复杂度为:O(N2)O(N^{2}),预计得分60

[满分算法]

当然要先把环求出来(如果直接求图的“直径”会出问题,自己可以想个反例),依然要枚举那一条边不选,现在的问题就是怎样快速处理
因为是个环,方便写程序有时也降低复杂度,我们把环拆开(像沙子合并那道DP一样,乘个2倍,然后拉开),那么现在就是一条链,链上一些点向下连了一棵子树(自己脑补吧ㄟ(▔,▔)ㄏ,ubuntu下画个简图真是麻烦)
那么我们假设链上KK个点,分别为1,2,3……1, 2, 3……,然后我们预处理出从链上这个点到子树的最长的路径记为DiD_{i},链上每两个点之间的距离为disijdis_{ij},根据两点间的距离求出每个点到第一个点的距离前缀和记做SiS_{i},那么第ii个点的子树到第jj的点的子树的最大距离为Sj−Sj+Di+DjS_{j} - S_{j} + D_{i} + D_{j},我们再移动几项:Di−Si+Dj+Sj D_{i} - S_{i}+ D_{j}+ S_{j} ,所以用线段树维护一个[L, R]中Di−Si+Dj+SjD_{i} - S_{i} + D_{j} + S{j} 的最大值,用O(log2N)O(log_{2}N)的时间得到答案。
那么我们NN次枚举,ANS=min(Query(i,i+K−1))(i∈[1,K])ANS = min(Query(i, i + K - 1)) (i \in [1, K])。
但是这还没有完,如果最长路并没有经过环呢(我们在用线段树维护的时候,维护的最小单元不是一个点,而是区间[i,i+1][i, i + 1]),所以,我们再和链上KK个点的子树中的直径求minmin即可(想想为什么不是maxmax)。
复杂度:O(Nlog2N)O(Nlog_{2}N),预计得分100

讲完辣 (〃^∇^)ぇ∧∧∧っ

最后附上代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;typedef long long lld;const int maxn = 1e5 + 13, maxm = maxn * 2;
const lld INF = 0x7fffffffffffffffll / 2ll;
int n, pos, head[maxm], used[maxn];
int c[maxn], size;
lld dis[maxm], s[maxm], d[maxm];
int l[maxm * 2], r[maxm * 2], rs[maxm * 2], ls[maxm * 2], root;
lld v1[maxm * 2], v2[maxm * 2], v[maxm * 2], maxSum;
queue<int>Q;
struct node {int v, w, last;
}line[maxn * 2];//超级读数
void read(int &a) {a = 0;bool judge = false;char c;while((c = getchar()) != EOF) {if(c == ' ' || c == '\n') {if(!judge) continue;return;}a = a * 10 + (c - '0');judge = true;}
}//链表存图
void my_read(int a, int b, int c) {line[++pos] = (node) {b, c, head[a]};head[a] = pos;
}//深搜找环
bool circleDfs(int pre, int u) {used[u] = true;for(int i = head[u]; i; i = line[i].last) {const int v = line[i].v;if(v == pre) continue;if(used[v]) {c[++size] = u;//s点保存前缀和s[size] = (lld)line[i].w;used[v] = false;return true;}bool judge = circleDfs(u, v);if(judge) {c[++size] = u;s[size] = s[size - 1] + (lld)line[i].w;if(used[u]) return true;return false;}}return false;
}void initForCircle() {circleDfs(1, 1);memset(used, 0, sizeof(used));for(int i = 1; i <= size; i++) used[c[i]] = true;
}//对链上每个点到树上求最长路径
lld getFarthestDis(int pre, int u) {lld far = 0ll;for(int i = head[u]; i; i = line[i].last) {const int v = line[i].v;if(v == pre || used[v]) continue;far = max(far, getFarthestDis(u, v) + (lld)line[i].w);}return far;
}//线段树标准模板
void builtTree(int &i, int A, int B) {i = ++pos;l[i] = A; r[i] = B;////最小单元应该是一个长度为1的区间if(A + 1 == B) {v1[i] = d[A] - s[A];v2[i] = d[B] + s[B];v[i] =  v1[i] + v2[i];return;}int mid = (A + B) >> 1;builtTree(ls[i], A, mid);builtTree(rs[i], mid, B);int LS = ls[i], RS = rs[i];v1[i] = max(v1[LS], v1[RS]);v2[i] = max(v2[LS], v2[RS]);v[i] = max(v[LS], v[RS]);v[i] = max(v[i], v1[LS] + v2[RS]);
}lld Query(int i, int A, int B) {int L = l[i], R = r[i];if(B <= L || A >= R) return 0;if(A <= L && R <= B) {lld temp;temp = max(v[i], maxSum + v2[i]);maxSum = max(v1[i], maxSum);return temp;}return max(Query(rs[i], A, B), Query(ls[i], A, B));
}int tot, temp[maxn];//找直径
int findDiameter(int pre, int u) {temp[++tot] = u;for(int i = head[u]; i; i = line[i].last) {const int v = line[i].v;if(v == pre || used[v]) continue;dis[v] = dis[u] + (lld)line[i].w;findDiameter(u, v);}
}//求每棵子树的直径
lld findLongestRoad(int x) {used[x] = false;int A; lld max1;tot = 0;dis[x] = 0;findDiameter(x, x);A = 0; max1 = 0ll;for(int i = 1; i <= tot; i++)if(max1 < dis[temp[i]]) max1 = dis[temp[i]], A = temp[i];for(int i = 1; i <= tot; i++) dis[temp[i]] = INF;tot = 0;dis[A] = 0;findDiameter(A, A);A = 0; max1 = 0ll;for(int i = 1; i <= tot; i++)if(max1 < dis[temp[i]]) max1 = dis[temp[i]];used[x] = true;return max1;
}int main() {freopen("foodshop.in", "r", stdin);
//  freopen("test.in", "w", stdout);scanf("%d", &n);for(int i = 1; i <= n; i++) {int a, b, c;read(a); read(b); read(c);my_read(a, b, c);my_read(b, a, c);}initForCircle();for(int i = 1; i <= size; i++) dis[c[i]] = getFarthestDis(c[i], c[i]);for(int i = 1; i <= size; i++) {d[i] = dis[c[i]]; d[i + size] = d[i];s[i + size] = s[size] + s[i];}pos = 0;builtTree(root, 1, size * 2);lld min1 = INF;for(int i = 1; i <= size; i++) {maxSum = -INF;lld temp = Query(root, i, i + size - 1);min1 = min(min1, temp);}for(int i = 1; i <= n; i++) dis[i] = INF;for(int i = 1; i <= size; i++) min1 = max(min1, findLongestRoad(c[i]));printf("%.1lf\n", (double)min1 / 2.0);return 0;
}

NOI2013快餐店【图上找环+线段树】相关推荐

  1. hdu5643 King's Game(约瑟夫环+线段树)

    Problem Description In order to remember history, King plans to play losephus problem in the parade ...

  2. P2444 [POI2000]病毒(ACAM上找环)

    LINK 给出一些010101串,问是否存在无限长的某个010101串 使得给定的任何一个010101串都不是它的子串 普通考虑好像不太好弄,我们建出ACAMACAMACAM看看 预处理ed[i]ed ...

  3. 浅谈线段树(Segment Tree)

    线段树的概念与性质 线段树首先是一棵树,而且是二叉树.树上的每个节点对应于一个区间[a,b],a,b通常为整数.同一层的节点所代表的区间,互相不重叠.并且同一层的区间加起来是连续的区间,叶子节点的区间 ...

  4. 线段树 --算法竞赛专题解析(24)

    本系列文章将于2021年整理出版.前驱教材:<算法竞赛入门到进阶> 清华大学出版社 网购:京东 当当   作者签名书:点我 有建议请加QQ 群:567554289 文章目录 1. 线段树概 ...

  5. 解题报告:P3834 【模板】可持久化线段树 2(主席树)详解

    P3834 [模板]可持久化线段树 2(主席树) 题解 P3834 [[模板]可持久化线段树 2(主席树)] 1)静态求第k大数 可持久化线段树,不能用堆的方法存子结点了,所以用指针l表示左儿子r表示 ...

  6. 数据结构之线段树入门(单点更新区间查询)

    线段树是学习数据结构必须学习的一种数据结构,在ACM,蓝桥等比赛中是经常出现的.利用线段树解题,会使得题目简单易理解.而且线段树是数据结构中比较基础而且用的很多的一种. 线段树定义 线段树是一种二叉搜 ...

  7. 线段树1——神奇的数据结构

    线段树,一种可以支持区间查询和区间修改的数据结构. 他可以在O(nlogn)的时间内实现查询某个区间的最大值,最小值,--还有很多神奇的内容. 现在,就让我们走进线段树的神奇世界吧! 注:全篇线段树以 ...

  8. ACM练级日志:可持久化线段树初级-POJ 2104

    近期决定把数据结构技能树继续开发下去,学习更深入层次的三项技能:可持久化线段树.动态树.树链剖分.然后那天看了看动态树,碎了,然后就去看可持久化线段树了-- 简单地说,这玩意是一个可以保存修改的历史版 ...

  9. bzoj 3489 A simple rmq problem——主席树套线段树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9b ...

最新文章

  1. rsync+inotify实现数据的实时备份
  2. 最近24小时记录:虚拟机与Wireshark 2.0
  3. mysql8 修改密码_sysbench压测软件连接mysql8失败案例分析
  4. 国内唯一,阿里云容器服务进入 Forrester 领导者象限
  5. Python之路--Python基础12--并发编程之协程
  6. 【Python面试】 说说Python变量、函数、类的命名规则?
  7. 企业能为员工储蓄点什么呢
  8. Hadoop学习之路(九)HDFS深入理解
  9. 哈工大大数据实验_科研常用 | 实验大数据分析方法
  10. android存到手机内存,android保存文件到手机内存
  11. python 归一化_数据的标准化和归一化
  12. 此处不允许使用分组函数_查找当前薪水排名第二多的员工信息(不使用order by和窗口函数)...
  13. [转载] Python中while循环的基本用法
  14. 安装MATLAB(已经下载安装包)
  15. Windows XP支持的最大内存是多少?
  16. 值得看三次的高干文_6本好看的高干文推荐,每本都值得看三次!
  17. 现在企业常用考勤软件
  18. 年轻时欠下风流情债的十大男女明星(组图)
  19. moment 时间类型的转换
  20. 个人小程序开发有哪些限制?

热门文章

  1. Python输出带逗号,不带中括号的列表
  2. Python数据科学库(三)
  3. oracle 保留池,ORACLE SGA之shared pool
  4. android os 2.3 微信,Android微信登陆
  5. 王者服务器维护7月21日,王者荣耀7月21日更新了什么_7月21日更新内容汇总2020_3DM手游...
  6. 初学 java 闲聊
  7. python程序员面试宝典 陈屹_Python 面试宝典
  8. 那些年苹果与 USB 的爱恨情仇
  9. 面试测试开发工程师:Java测试基础篇
  10. Linux检查空口令