洛谷P1399 快餐店
题意:在基环树上找一点,使得这个点到所有点的距离最大值最小。这个点可以在某条边上。
解:很容易想到找出直径然后/2对吧...这里的直径是指任意两点间最短距离的最大值。
然而我这个SB冥思苦想了半天之后想到了一个傻逼绝伦的做法:枚举边!
这个点一定在某条边上。
所以知道边的两端点最长延伸多长即可。
如果是子树里的边,很显然下面那个点就是子树内最长链。而上面那个点就是子树外最长链或深度 + 根节点在环上最长延伸距离。
如果是环上的边,就是两端点子树最长链或者环上延伸的最长距离。
值得注意的是,这两个"环上最长延伸距离"并不是一样的。
因为前者只有一个点在环上,没有别的环上的点跟它竞争。
而后者要跟环上另一个点相互竞争。所以还要分两种情况。
具体实现上,首先找环,然后每个子树做两次树形DP,第二次是二次扫描与换根法。
然后把环的信息提取出来DP,利用单调队列来求最长延伸距离,反正我写了4个......
最后枚举边判定。
反正就是仔细写,耐心Debug...
(为什么别人1k就能A而我要5k啊...)
#include <cstdio>
#include <algorithm>
#include <stack>
#include <cstring> typedef long long LL;
const int N = ; struct Edge {
int nex, v;
LL len;
}edge[N << ]; int top = ; int e[N], n, cir[N], tc, p[N], head, tail, fr[N], nex[N], pre[N];
std::stack<int> S;
bool vis[N], is_cir[N];
LL d[N], len1[N], len2[N], lenup[N], dis[N], p2[N], sum[N], Long[N], Long_l[N], Long_r[N]; inline void add(int x, int y, LL z) {
top++;
edge[top].v = y;
edge[top].len = z;
edge[top].nex = e[x];
e[x] = top;
return;
} void Df(int x, int f) {
vis[x] = ;
S.push(x);
for(int i = e[x]; i && !vis[]; i = edge[i].nex) {
int y = edge[i].v;
if(y == f) {
continue;
}
if(vis[y]) {
vis[] = ;
while(x != y) {
x = S.top();
S.pop();
is_cir[x] = ;
cir[++tc] = x;
}
return;
}
Df(y, x);
}
if(vis[]) {
return;
}
S.pop();
vis[x] = ;
return;
} void DFS_1(int x, int f, int aim) { // get d len1 len2
if(!fr[x]) {
fr[x] = fr[f];
}
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == f || is_cir[y]) {
if(cir[aim] == y) {
dis[aim] = edge[i].len;
nex[x] = y;
pre[y] = x;
}
continue;
}
d[y] = d[x] + edge[i].len;
DFS_1(y, x, aim);
if(len1[x] < len1[y] + edge[i].len) {
len2[x] = len1[x];
len1[x] = len1[y] + edge[i].len;
}
else if(len2[x] < len1[y] + edge[i].len) {
len2[x] = len1[y] + edge[i].len;
}
}
return;
} void DFS_2(int x, int f) {
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == f || is_cir[y]) {
continue;
}
if(len1[y] + edge[i].len == len1[x]) {
lenup[y] = std::max(lenup[x], len2[x]) + edge[i].len;
}
else {
lenup[y] = std::max(lenup[x], len1[x]) + edge[i].len;
}
DFS_2(y, x);
}
return;
} int main() {
int x, y;
LL z, Sum = ;
double ans = ;
scanf("%d", &n);
for(int i = ; i <= n; i++) {
scanf("%d%d%lld", &x, &y, &z);
add(x, y, z);
add(y, x, z);
ans += z;
}
Df(, ); for(int i = ; i <= tc; i++) {
fr[cir[i]] = cir[i];
DFS_1(cir[i], , (i == tc ? : i + ));
DFS_2(cir[i], );
cir[tc + i] = cir[i];
}
for(int i = ; i <= tc; i++) {
Sum += dis[i];
dis[tc + i] = dis[i];
}
for(int i = ; i <= tc * ; i++) {
sum[i] = sum[i - ] + dis[i];
} LL dt = ;
head = ;
tail = ;
for(int i = ; i <= tc * ; i++) {
// DP
while(head <= tail && sum[i] - sum[p[head]] > Sum - (sum[i] - sum[p[head]])) {
head++;
}
dt += dis[i];
if(head <= tail) {
Long[cir[i]] = std::max(Long[cir[i]], p2[head] + dt);
}
while(head <= tail && len1[cir[i]] - dt >= p2[tail]) {
tail--;
}
p[++tail] = i;
p2[tail] = len1[cir[i]] - dt;
}
head = ;
tail = ;
dt = ;
for(int i = tc * ; i >= ; i--) {
while(head <= tail && sum[p[head]] - sum[i] > Sum - (sum[p[head]] - sum[i])) {
head++;
}
dt += dis[i + ];
if(head <= tail) {
Long[cir[i]] = std::max(Long[cir[i]], p2[head] + dt);
}
while(head <= tail && len1[cir[i]] - dt >= p2[tail]) {
tail--;
}
p[++tail] = i;
p2[tail] = len1[cir[i]] - dt;
}
// -------------------------------------------------------------------------------------------------------
head = ;
tail = ;
dt = ;
dis[tc * + ] = dis[];
for(int i = ; i <= tc * ; i++) {
// DP
while(head <= tail && sum[i] - sum[p[head]] > Sum - (sum[i] - sum[p[head]] + dis[i + ])) {
head++;
}
dt += dis[i];
if(head <= tail) {
Long_l[cir[i]] = std::max(Long_l[cir[i]], p2[head] + dt);
}
while(head <= tail && len1[cir[i]] - dt >= p2[tail]) {
tail--;
}
p[++tail] = i;
p2[tail] = len1[cir[i]] - dt;
}
head = ;
tail = ;
dt = ;
for(int i = tc * ; i >= ; i--) {
while(head <= tail && sum[p[head]] - sum[i] > Sum - (sum[p[head]] - sum[i - ])) {
head++;
}
dt += dis[i + ];
if(head <= tail) {
Long_r[cir[i]] = std::max(Long_r[cir[i]], p2[head] + dt);
}
while(head <= tail && len1[cir[i]] - dt >= p2[tail]) {
tail--;
}
p[++tail] = i;
p2[tail] = len1[cir[i]] - dt;
}
//
for(int i = ; i <= top; i += ) {
int x = edge[i].v, y = edge[i ^ ].v;
LL a, b;
if(is_cir[x] && is_cir[y]) {
if(nex[y] == x) {
std::swap(x, y);
}
a = std::max(Long_l[x], len1[x]);
b = std::max(Long_r[y], len1[y]);
}
else {
if(d[x] > d[y]) {
std::swap(x, y);
}
a = len1[y];
b = std::max(lenup[y] - edge[i].len, d[x] + Long[fr[x]]);
}
if(a < b) {
std::swap(a, b);
}
if(a >= edge[i].len + b) {
ans = std::min(ans, (double)a);
}
else {
ans = std::min(ans, (a + b + edge[i].len) / 2.0);
}
} printf("%.1f", ans);
return ;
}
AC代码
洛谷P1399 快餐店的更多相关文章
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
- 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.
没有上司的舞会 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...
- 洛谷P1108 低价购买[DP | LIS方案数]
题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...
- 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP
题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...
- 洛谷P1710 地铁涨价
P1710 地铁涨价 51通过 339提交 题目提供者洛谷OnlineJudge 标签O2优化云端评测2 难度提高+/省选- 提交 讨论 题解 最新讨论 求教:为什么只有40分 数组大小一定要开够 ...
- 洛谷P1371 NOI元丹
P1371 NOI元丹 71通过 394提交 题目提供者洛谷OnlineJudge 标签云端评测 难度普及/提高- 提交 讨论 题解 最新讨论 我觉得不需要讨论O long long 不够 没有取 ...
- 洛谷P1538迎春舞会之数字舞蹈
题目背景 HNSDFZ的同学们为了庆祝春节,准备排练一场舞会. 题目描述 在越来越讲究合作的时代,人们注意的更多的不是个人物的舞姿,而是集体的排列. 为了配合每年的倒计时,同学们决定排出——“数字舞蹈 ...
- 洛谷八月月赛Round1凄惨记
个人背景: 上午9:30放学,然后因为学校举办读书工程跟同学去书城选书,中午回来开始打比赛,下午又回老家,中间抽出一点时间调代码,回家已经8:50了 也许是7月月赛时“连蒙带骗”AK的太幸运然而因同学 ...
- 洛谷 P1379 八数码难题 Label:判重&&bfs
特别声明:紫书上抄来的代码,详见P198 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给 ...
随机推荐
- 剑指offer(4)
题目: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2 ...
- 连接mysql 出现 1005 error(150) , error(121)的错误
1.显示不能创建表 出现150错误 将检查是因为 我的user 表示拷贝过来的所以它设置的编码格式是utf-8 而我又新创建的表没有添加编码格式,所以它认为这两个关联的表之间的编码格式不匹配. 2.出 ...
- Session和Cookie介绍及常见httpcode
Cookie和Session,及常见httpcode 1.cookie和session简介: cookie是放在客户端的键值对,用来识别用户信息的,主要包括:名字,值,过期时间,路径和域.路径与域一起 ...
- Java HashMap的put操作(Java1.8)
https://www.cnblogs.com/JzedyBlogs/p/10208295.html 写得非常好: 这个是Java1.8 ------------------------------- ...
- Java之XML操作:从XML中直接获取数据
本文介绍如何将数据记录在XML文件中,然后通过DOM4J直接从XML中读取到数据. 依赖包: <dependency> <groupId>dom4j</groupId&g ...
- vue 使用技巧总结 19.01
组件中箭头函数的使用 不要使用箭头来定义任意生命周期钩子函数: 不要使用箭头来定义一个 methods 函数: 不要使用箭头来定义 computed 里的函数: 不要使用箭头定义 watch 里的函数 ...
- 原子变量与CAS算法(二)
一.锁机制存在的问题 (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2)一个线程持有锁会导致其它所有需要此锁的线程挂起. (3)如果一个优先级高的线程等待一个 ...
- Android 自定义ListView 修改数据
当我们修改了 自定义ListView,如何更新界面上的控件呢? 两种方法: 1 重新绑定adapter (不推荐) Adapter_InboundPO adapter =(Adapter_Inboun ...
- 数据库 -- mysql表操作
一,存储引擎介绍 存储引擎即表类型,mysql根据不同的表类型会有不同的处理机制 详见:https://www.cnblogs.com/peng104/p/9751738.html 二,表介绍 表相当 ...
- 大学jsp实验4include,forword
一.实验目的与要求 1.掌握常用JSP动作标记的使用. 二.实验内容 1.include动作标记的使用 编写一个名为shiyan4_1.jsp的JSP页面,页面内容自定,但要求使用include动作标 ...