题意

给定一颗带边权的树,求一条边数在\(L\)、\(R\)之间的路径,并使得路径上边权的中位数最大。输出一条可行路径的两个端点。这里若有偶数个数,中位数为中间靠右的那个。

\(n, L, R\leq 10^5\)

题解

看一眼是点分。然后发现中位数要二分,把\(\geq mid\)的权值设为\(1\),\(<mid\)的设为\(-1\),问题转换为找边权\(\geq 0\)的路径

易发现一个子树,每个深度存一个最大值就行

考虑怎么合并两个子树:假设之前子树答案是\(f\),\(f[d]\)表示深度为\(d\)的边权最大和。当前为\(g\)。

再考虑\(g[i]\)的时候,能与他配对的\(f\)实际上是区间\(f[L-i]\)到\(f[R-i]\),并且这个区间随着\(i\)增大而减小

用单调队列维护最大值即可。

注意子树要按深度从小到大排序,不然复杂度会被卡成平方!

时间复杂度\(O(n \log ^2n)\)。实现注意细节。

#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 1e5 + 10;
const int INF = 1e7 + 10;
struct Edge { int v, w; };
int n, m, ans, ansu, ansv, L, R;
int size, rt, nowu, nowv, w[N], sz[N], tw[N];
vector<Edge> G[N];
bool vis[N];
void findrt(int u, int fa = 0) {
sz[u] = 1; w[u] = 0;
for(const Edge &e : G[u]) {
if(e.v != fa && !vis[e.v]) {
findrt(e.v, u);
sz[u] += sz[e.v];
w[u] = max(w[u], sz[e.v]);
}
}
w[u] = max(w[u], size - sz[u]);
if(!rt || w[u] < w[rt]) rt = u;
}
int d[N], cur_max, curd[N], fw[N];
void dfs0(int u, int fa = 0) {
cur_max = max(cur_max, d[u] = d[fa] + 1);
for(const Edge &e : G[u]) {
if(e.v != fa && !vis[e.v]) {
dfs0(e.v, u);
}
}
}
int st[N], top, low, f[N], g[N], fu[N], gu[N];
bool cmp(int x, int y) {
return curd[x] < curd[y];
}
void dfs(int u, int we, int fa = 0) {
if(g[d[u]] < we) {
g[d[u]] = we; gu[d[u]] = u;
}
for(const Edge &e : G[u]) if(e.v != fa && !vis[e.v]) {
dfs(e.v, we + (e.w >= low ? 1 : -1), u);
}
}
bool calc(int u, int mid) {
top = d[u] = 0;
for(const Edge &e : G[u]) if(!vis[e.v]) {
cur_max = 0; dfs0(e.v, u);
curd[e.v] = cur_max;
st[++ top] = e.v; fw[e.v] = e.w;
}
sort(st + 1, st + top + 1, cmp);
int ld = -1; low = mid;
for(int i = 1; i <= top; i ++) {
int v = st[i], nd = curd[v];
fill(g + 1, g + nd + 1, - INF);
dfs(v, fw[v] >= low ? 1 : -1);
for(int j = L; j <= nd && j <= R; j ++)
if(g[j] >= 0) return nowu = u, nowv = gu[j], 1;
if(i == 1) {
copy(g + 1, g + nd + 1, f + 1);
copy(gu + 1, gu + nd + 1, fu + 1);
} else {
static int q[N];
int ql = 0, qr = 0, pos = min(R - 1, ld); //!
for(int j = 1; j <= nd; j ++) {
for(; pos >= L - j && pos >= 1; pos --) {
for(; ql < qr && f[q[qr - 1]] <= f[pos]; qr --) ;
q[qr ++] = pos;
}
for(; ql < qr && q[ql] > R - j; ql ++) ;
if(ql < qr && f[q[ql]] + g[j] >= 0)
return nowu = fu[q[ql]], nowv = gu[j], 1;
}
for(int j = 1; j <= ld; j ++) if(f[j] < g[j]) f[j] = g[j], fu[j] = gu[j];
for(int j = ld + 1; j <= nd; j ++) f[j] = g[j], fu[j] = gu[j];
}
ld = nd;
}
return 0;
}
void solve(int u) {
int tsz = size; vis[u] = 1;
int l = 1, r = m, uu = -1, vv = -1, res = -1;
while(l <= r) {
int mid = (l + r) >> 1;
if(calc(u, tw[mid])) {
l = (res = mid) + 1;
uu = nowu; vv = nowv;
} else r = mid - 1;
}
if(res > ans) {
ans = res; ansu = uu; ansv = vv;
}
for(const Edge &e : G[u]) {
if(!vis[e.v]) {
rt = 0; size = sz[e.v] > sz[u] ? tsz - sz[u] : sz[e.v];
findrt(e.v); solve(rt);
}
}
}
int main() {
scanf("%d%d%d", &n, &L, &R);
for(int u, v, w, i = 1; i < n; i ++) {
scanf("%d%d%d", &u, &v, &w);
G[u].push_back({v, w});
G[v].push_back({u, w});
tw[i] = w;
}
sort(tw + 1, tw + n);
m = unique(tw + 1, tw + n) - tw - 1;
rt = 0; size = n; findrt(1); solve(rt);
printf("%d %d\n", ansu, ansv);
return 0;
}

「CF150E」Freezing with Style「点分治」「单调队列」的更多相关文章

  1. [P2216] [HAOI2007]理想的正方形 「单调队列」

    思路:用单调队列分别维护行与列. 具体实现方法:是先用单调队列对每一行的值维护,并将a[][]每个区间的最大值,最小值分别存在X[][]和x[][]中. 那么X[][]与x[][]所存储的分别是1×n ...

  2. [POJ2823] Sliding Window 「单调队列」

    我们从最简单的问题开始: 给定一个长度为N的整数数列a(i),i=0,1,...,N-1和窗长度k. 要求:   f(i) = max{ a(i-k+1),a(i-k+2),..., a(i) },i ...

  3. 「CF986F」 Oppa Funcan Style Remastered

    「CF986F」 Oppa Funcan Style Remastered Link 首先发现分解成若干个 \(k\) 的因数很蠢,事实上每个因数都是由某个质因子的若干倍组成的,所以可以将问题转换为分 ...

  4. loj#2483. 「CEOI2017」Building Bridges 斜率优化 cdq分治

    loj#2483. 「CEOI2017」Building Bridges 链接 https://loj.ac/problem/2483 思路 \[f[i]=f[j]+(h[i]-h[j])^2+(su ...

  5. docker并不能把部署的工作「减少为0」,比较好的情况下是「基本减少为1」

    很多人说docker改变了运维世界,这句话是从群体角度来说的,是统计学意义上的改变,像mysql,python这样被大规模使用的基础应用,docker化之后为整个群体所节省的时间是非常巨大的. 有人可 ...

  6. 「单调队列优化DP」P2034 选择数字

    「单调队列优化DP」P2034 选择数字 题面描述: 给定一行n个非负整数a[1]..a[n].现在你可以选择其中若干个数,但不能有超过k个连续的数字被选择.你的任务是使得选出的数字的和最大. 输入格 ...

  7. CodeForces 150E: Freezing with Style

    题目传送门:CF150E. 据说这个傻逼题还有一个 \(\log\) 的做法,但是我还不会. 题意简述: 给定一棵 \(n\)(\(2\le n\le 10^5\))个点的树,边有边权. 定义一条路径 ...

  8. 「GM_脚本」获取 GitHub 项目文件的 jsDelivr CDN 地址「好像没啥用系列」

    基本信息: name:「 GitHub 」获取文件的 jsDelivr 地址 desc:获取项目文件的 CDN 地址 url: https://github.com/wdssmq/userscript ...

  9. loj#2312. 「HAOI2017」八纵八横(线性基 线段树分治)

    题意 题目链接 Sol 线性基+线段树分治板子题.. 调起来有点自闭.. #include<bits/stdc++.h> #define fi first #define se secon ...

随机推荐

  1. Docker 学习笔记(三):数据、网络、系统权限、docker-compose

    一.Docker 数据管理 Docker 持久化数据有两种方式: 使用数据卷:更安全,和主机耦合度低 将主机的目录挂载到容器中:更方便,主机和容器可以很方便地交换数据. 数据卷相关的命令: docke ...

  2. jmeter中生成UUID作为唯一标识符

    在测试过程中,我们有时候需要一个唯一不重复的值(比如order_id).我之前一直用的时间戳+计数器/随机函数拼接,但是有时候效果不太好,今天知道了UUID这玩意,可以来操作下.jmeter也提供了U ...

  3. vsCode 代码不高亮显示的问题——安装Vetur插件

    vsCode 代码不高亮显示: 解决办法:安装Vetur插件 点击左侧菜单的扩展-->搜索Vetur-->点击安装-->安装完成重启vsCode

  4. 在论坛中出现的比较难的sql问题:30(row_number函数 物料组合问题)

    原文:在论坛中出现的比较难的sql问题:30(row_number函数 物料组合问题) 在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所 ...

  5. 基于【 springBoot +springCloud+vue 项目】一 || 项目架构简介

    一.前言 基于前期学习以及工作经验积累,持续更新基于springboot+springcloud+vue的demo项目.

  6. 使用Django时需要注意的八个要点

    1.在settings.py中使用os. path.dirname() 常用代码如下: # settings.py import os PROJECT_DIR = os.path.dirname(__ ...

  7. 手写DAO框架(五)-DAO层实现

    -------前篇:手写DAO框架(四)-SQL执行--------- 前言 通过上一篇,可以通过传入sql和对应的参数,可以执行sql并返回结果.但是对于一个DAO框架来说,要尽量的面向对象编程,也 ...

  8. 在pivotal cloud foundry上申请账号和部署应用

    Created by Wang, Jerry, last modified on Jul 04, 2016 URL: http://run.pivotal.io/ maintain your mobi ...

  9. win10家庭版本不能连接远程桌面

    出现身份验证错误 要求的函数不受支持,CredSSP 加密 Oracle 修正 然后网上大多数教程是叫你修改组策略,然后试过坑的我发现自己的家庭版没有组策略,天真的我在网上找寻了装组策略的代码,一顿捣 ...

  10. C实现哈希表

    1 哈希表原理 这里不讲高深理论,只说直观感受.哈希表的目的就是为了根据数据的部分内容(关键字),直接计算出存放完整数据的内存地址. 试想一下,如果从链表中根据关键字查找一个元素,那么就需要遍历才能得 ...