\(sol:\)

给一种大常数 \(n \log^2 n\) 的做法

考虑二分,由于是中位数,我们就二分这个中位数,\(x>=mid\)则设为 \(1\),否则为 \(-1\) 所以我们只需要找到一条 \(sum >= 0\) 的路径,这样就有解了,易证。

长链剖分,让长链变成连续的一段区间 \([dfn_u,dfn_u+len_u-1]\),线段树的每个点是对于当前的 \(u\)

然后考虑到对于每个 \(u\) 只需要找到长度在 \([L,R]\) 的边,且经过 \(u\),很显然是从 \(u\) 的子树里面找,显然你只需要算出来先前每层的\(\max\)存在线段树上面,表示 \(dep_v\) 的一堆点,到 \(u\) 的最大路径,然后用 \(dfs\)+线段树,从下到上更新,每次把 \(\max\) 更新到长链相对的线段树区间 \([dfn_u,dfn_u+len_u-1]\) 上面。

考虑到更新答案什么的,直接暴力更新长链上的信息(复杂度证明在下面)

即枚举一个长度 \(j\),然后你另一条边的长度区间是限定的,于是你可以线段树区间查询,所以每次查询的复杂度都是 \(\log n\)

每次查询完之后更新相同深度的答案,这样就可以保证不会重复了,复杂度仍然是优美的一个 \(\log\)。

复杂度分析

考虑到它的 \(dfs\) 是枚举非儿子点的最深深度,而你非儿子点的一定是某个长链的 \(top\),那么你保证了只会遍历一个点一遍,于是就可以证明这个复杂度是 \(O(n)\) 的,但是由于你必须要用一个线段树来维护,所以单次的复杂度就到达了 \(O(n \log n)\),外边还需要一个二分,复杂度是 \(O(n \log^2 n)\)

#include <cstdio>
#include <algorithm> int read() {
int x = 0;
char c = getchar();
while (c < 48) c = getchar();
while (c > 47) x = x * 10 + (c - 48), c = getchar();
return x;
} int min(int x, int y) { return x < y ? x : y; }
int max(int x, int y) { return x > y ? x : y; } int n, L, R;
// edge-list
const int maxn = 2e5 + 52;
struct edge {
int v, nxt, w;
} e[maxn << 1];
int head[maxn], cnt = 0, val[maxn];
void add(int u, int v, int w) {
e[++cnt] = { v, head[u], w }, head[u] = cnt;
e[++cnt] = { u, head[v], w }, head[v] = cnt;
} // dfs
int len[maxn], son[maxn], wt[maxn];
void dfs(int u, int fa) {
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa) continue;
dfs(v, u);
if (len[v] > len[son[u]]) {
son[u] = v, wt[u] = e[i].w;
}
}
len[u] = len[son[u]] + 1;
} int dfn[maxn], idx = 0;
void dfs(int u) {
dfn[u] = ++idx;
if (son[u]) dfs(son[u]);
for (int i = head[u]; i; i = e[i].nxt)
if (!dfn[e[i].v]) dfs(e[i].v);
}
// smt
struct node {
int mx, t;
} sum[maxn << 2];
int tag[maxn << 2];
node merge(const node& x, const node& y) { return x.mx > y.mx ? x : y; } void clr(int l, int r, int rt) {
tag[rt] = sum[rt].t = 0, sum[rt].mx = -n;
if (l == r) return;
int mid = l + r >> 1;
clr(l, mid, rt << 1);
clr(mid + 1, r, rt << 1 | 1);
} void pushtag(int rt, int v) { tag[rt] += v, sum[rt].mx += v; }
void pushd(int rt) {
if (!tag[rt]) return;
pushtag(rt << 1, tag[rt]);
pushtag(rt << 1 | 1, tag[rt]);
tag[rt] = 0;
} node qry(int a, int b, int l, int r, int rt) {
if (a <= l && r <= b) return sum[rt];
pushd(rt);
int mid = l + r >> 1;
if (b <= mid) return qry(a, b, l, mid, rt << 1);
if (a > mid) return qry(a, b, mid + 1, r, rt << 1 | 1);
return merge(qry(a, b, l, mid, rt << 1), qry(a, b, mid + 1, r, rt << 1 | 1));
} void change(int a, int b, int l, int r, int rt, int v) {
if (a <= l && r <= b) {
pushtag(rt, v);
return;
}
pushd(rt);
int mid = l + r >> 1;
if (a <= mid) change(a, b, l, mid, rt << 1, v);
if (b > mid) change(a, b, mid + 1, r, rt << 1 | 1, v);
sum[rt] = merge(sum[rt << 1], sum[rt << 1 | 1]);
} void modify(int l, int r, int rt, int x, node v) {
if (l == r) {
sum[rt] = merge(sum[rt], v);
return;
}
pushd(rt);
int mid = l + r >> 1;
if (x <= mid)
modify(l, mid, rt << 1, x, v);
else
modify(mid + 1, r, rt << 1 | 1, x, v);
sum[rt] = merge(sum[rt << 1], sum[rt << 1 | 1]);
} int flag = 0, xx = 0, yy = 0;
void dfs(int u, int fa, int mid) {
if (flag) return;
modify(1, n, 1, dfn[u], { 0, u });
if (!son[u]) return;
dfs(son[u], u, mid);
change(dfn[u] + 1, dfn[u] + len[u] - 1, 1, n, 1, wt[u] >= mid ? 1 : -1);
if (L < len[u]) {
node ask = qry(dfn[u] + L, dfn[u] + min(len[u] - 1, R), 1, n, 1);
if (ask.mx >= 0) {
flag = 1;
xx = u, yy = ask.t;
return;
}
}
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa || v == son[u]) continue;
dfs(v, u, mid);
int w = e[i].w >= mid ? 1 : -1;
for (int j = 1; j <= len[v]; j++) {
node c = qry(dfn[v] + j - 1, dfn[v] + j - 1, 1, n, 1);
c.mx += w;
if (L - j >= len[u] || j > R) continue;
node ask = qry(dfn[u] + max(0, L - j), dfn[u] + min(len[u] - 1, R - j), 1, n, 1);
if (c.mx + ask.mx >= 0) {
xx = c.t, yy = ask.t;
flag = 1;
break;
}
}
for (int j = 1; j <= len[v]; j++) {
node c = qry(dfn[v] + j - 1, dfn[v] + j - 1, 1, n, 1);
c.mx += w, modify(1, n, 1, dfn[u] + j, c);
}
}
}
bool chk(int mid) {
clr(1, n, 1);
flag = 0, dfs(1, 0, mid);
return flag;
}
int main() {
// freopen("testdata.in", "r", stdin);
n = read(), L = read(), R = read();
for (int i = 1; i < n; i++) {
int u = read(), v = read(), w = read();
add(u, v, w), val[i] = w;
}
dfs(1, 0), dfs(1), std ::sort(val + 1, val + n);
int le = 1, ri = std ::unique(val + 1, val + n) - val - 1;
int ansx = 0, ansy = 0;
while (le <= ri) {
int mid = le + ri >> 1;
if (chk(val[mid])) {
le = mid + 1;
ansx = xx, ansy = yy;
} else
ri = mid - 1;
}
printf("%d %d\n", ansx, ansy);
return 0;
}

CF 150E Freezing with Style [长链剖分,线段树]的更多相关文章

  1. BZOJ.1758.[WC2010]重建计划(分数规划 点分治 单调队列/长链剖分 线段树)

    题目链接 BZOJ 洛谷 点分治 单调队列: 二分答案,然后判断是否存在一条长度在\([L,R]\)的路径满足权值和非负.可以点分治. 对于(距当前根节点)深度为\(d\)的一条路径,可以用其它子树深 ...

  2. 2018牛客网暑假ACM多校训练赛(第七场)I Tree Subset Diameter 动态规划 长链剖分 线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round7-I.html 题目传送门 -  https://www.n ...

  3. BZOJ.3653.谈笑风生(长链剖分/线段树合并/树状数组)

    BZOJ 洛谷 \(Description\) 给定一棵树,每次询问给定\(p,k\),求满足\(p,a\)都是\(b\)的祖先,且\(p,a\)距离不超过\(k\)的三元组\(p,a,b\)个数. ...

  4. BZOJ1758[Wc2010]重建计划——分数规划+长链剖分+线段树+二分答案+树形DP

    题目描述 输入 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai, ...

  5. BZOJ.3252.攻略(贪心 长链剖分/线段树)

    题目链接 贪心,每次选价值最大的一条到根的链.比较显然(不选白不选). 考虑如何维护这个过程.一个点的价值选了就没有了,而它只会影响它子树里的点,可以用DFS序+线段树修改.而求最大值也可以用线段树. ...

  6. 2019.01.21 bzoj1758: [Wc2010]重建计划(01分数规划+长链剖分+线段树)

    传送门 长链剖分好题. 题意简述:给一棵树,问边数在[L,R][L,R][L,R]之间的路径权值和与边数之比的最大值. 思路: 用脚指头想都知道要01分数规划. 考虑怎么checkcheckcheck ...

  7. [WC2010]重建计划(长链剖分+线段树+分数规划)

    看到平均值一眼分数规划,二分答案mid,边权变为w[i]-mid,看是否有长度在[L,R]的正权路径.设f[i][j]表示以i为根向下j步最长路径,用长链剖分可以优化到O(1),查询答案线段树即可,复 ...

  8. 洛谷 P4292 - [WC2010]重建计划(长链剖分+线段树)

    题面传送门 我!竟!然!独!立!A!C!了!这!道!题!incredible! 首先看到这类最大化某个分式的题目,可以套路地想到分数规划,考虑二分答案 \(mid\) 并检验是否存在合法的 \(S\) ...

  9. P4292-[WC2010]重建计划【长链剖分,线段树,0/1分数规划】

    正题 题目链接:https://www.luogu.com.cn/problem/P4292 题目大意 给出\(n\)个点的一棵树,然后求长度在\([L,U]\)之间的一条路径的平均权值最大. 解题思 ...

随机推荐

  1. 二、Django学习之增删改查

    增加数据 第一种方式 def index(request): #创建记录方式1 #实例化要添加的记录(对象) student_obj = models.Student( name='dazhuang' ...

  2. 最好用的web端代码文本编辑器ACE

    使用足够简单,功能足够强大,体验足够优秀 之前有一个系列文章介绍我在运维系统开发过程中用到的那些顺手的前端插件,总共发了四篇文章介绍了三个非常棒的插件,分别是bootstrap-duallistbox ...

  3. 并发队列之DelayQueue

    已经说了四个并发队列了,DelayQueue这是最后一个,这是一个无界阻塞延迟队列,底层基于前面说过的PriorityBlockingQueue实现的 ,队列中每个元素都有过期时间,当从队列获取元素时 ...

  4. 八使用Shell函数

    在Shell脚本中,将一些需要重复使用的操作,定义为公共的语句块,即可称为函数 使用函数的好处? 使脚本代码更简洁,增强易读性 提高Shell脚本的执行效率 函数定义方法 基本格式1 function ...

  5. 实验5: IOS的升级与恢复

    实验5:  IOS的升级与恢复 实验目的 通过本实验可以掌握 1)    掌握IOS 正常的情况下升级IOS2)    IOS 丢失的情况下使用TFTP恢复IOS3)    IOS 丢失的情况下使用X ...

  6. FFMPEG学习----打印视频信息

    FFMPEG学习资料少之又少,在此推荐雷神的博客: http://blog.csdn.net/leixiaohua1020 在这里,我们把打印视频里的相关信息作为学习FFMPEG的 Hello Wor ...

  7. CCF_201312-5_I’m stuck!

    一次bfs从起点开始找到起点能到达的点,一次bfs从终点开始找到能到终点的点,最后输出答案即可. 刚开始写的时候,考虑找起点能到达的点的时候,用了dfs,提交只有20分,仔细想了一下,会存在无限循环的 ...

  8. 深入分析Java反射(三)-泛型

    前提 Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行 ...

  9. tomcat-windows10环境搭建

    1.进入Tomcat官网Apache Tomcat® - Welcome! 2.根据操作系统选择合适的版本下载 zip用于windows操作系统, tar.gz用于unix和linux操作系统 Bin ...

  10. T1飞跃树林 && 【最长等差子序列】

    solution by Mr.gtf 一道简单的递推 首先我们对树高从大到小排序 很容易得到递推式 ans[i]=Σans[j] (j<i && h[j]-h[i]<=K) ...