题意

给定一颗带边权的树,求一条边数在\(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. Oracle和SQL Server 用当前日期减去 '0001-01-01' 得出的天数不一致,相差2天,谁知道原因?

    Oracle和SQL Server 用当前日期减去 '0001-01-01' 得出的天数不一致,相差2天.求大佬科普

  2. SPA中使用jwt

    什么是jwt? JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案 JWT的工作原理 1. 是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,示例如下:{&q ...

  3. Arm-Linux 移植 mtd-utils 1.x

    有关文章:<mtd-utils 的 使用> 背景: 关于在公司的生产环境有关.不希望每次都在uboot下面做nand flash 的烧写:也觉得使用U盘升级的方法比较慢,而且有关的驱动不是 ...

  4. P1777 帮助_NOI导刊2010提高(03)

    也许更好的阅读体验 \(\mathcal{Description}\) Bubu的书架乱成一团了!帮他一下吧! 他的书架上一共有n本书.我们定义混乱值是连续相同高度书本的段数.例如,如果书的高度是30 ...

  5. sql server split切割字符串

    create FUNCTION [dbo].[dnt_split] ( @splitstring varchar(max), @separator CHAR() = ',' ) RETURNS @sp ...

  6. C# 中的匿名函数使用

    需求:在图一的callback函数中,我需要使用4个参数,但是又不想把四个参数都传入到requestImg 里面,可以采用上面的 匿名函数的做法.

  7. Oracle查看占用表空间最大的表(排序)

    场景:在做数据库巡检时,检查大表是必不可少的操作,可以查看各表占用表空间的大小 代码: as sizes,q.num_rows,t.segment_type from dba_segments t l ...

  8. java - day019 - 数据库

    https://www.cnblogs.com/myxq666/p/7787744.html Mac 安装MySQL步骤 什么是数据库 数据库: 英文名称Database ,简称 DB 数据库是按照数 ...

  9. sql的时间格式

    sql中的时间格式转换主要有:date_format函数,str_to_date函数 1. 首先选择一个数据库 use db_name; 2. 显示当前时区的时间: SELECT NOW(); 3. ...

  10. MyBatis3_[tp-26-27]_映射文件_select_返回List_记录封装Map:返回单个元素的Map或者整体Map集合

    笔记要点出错分析与总结工程组织 1.定义接口 public interface EmployeeMapper { //多条记录封装到一个map中: Map<Integer,Employee> ...