「CF150E」Freezing with Style「点分治」「单调队列」
题意
给定一颗带边权的树,求一条边数在\(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「点分治」「单调队列」的更多相关文章
- [P2216] [HAOI2007]理想的正方形 「单调队列」
思路:用单调队列分别维护行与列. 具体实现方法:是先用单调队列对每一行的值维护,并将a[][]每个区间的最大值,最小值分别存在X[][]和x[][]中. 那么X[][]与x[][]所存储的分别是1×n ...
- [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 ...
- 「CF986F」 Oppa Funcan Style Remastered
「CF986F」 Oppa Funcan Style Remastered Link 首先发现分解成若干个 \(k\) 的因数很蠢,事实上每个因数都是由某个质因子的若干倍组成的,所以可以将问题转换为分 ...
- 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 ...
- docker并不能把部署的工作「减少为0」,比较好的情况下是「基本减少为1」
很多人说docker改变了运维世界,这句话是从群体角度来说的,是统计学意义上的改变,像mysql,python这样被大规模使用的基础应用,docker化之后为整个群体所节省的时间是非常巨大的. 有人可 ...
- 「单调队列优化DP」P2034 选择数字
「单调队列优化DP」P2034 选择数字 题面描述: 给定一行n个非负整数a[1]..a[n].现在你可以选择其中若干个数,但不能有超过k个连续的数字被选择.你的任务是使得选出的数字的和最大. 输入格 ...
- CodeForces 150E: Freezing with Style
题目传送门:CF150E. 据说这个傻逼题还有一个 \(\log\) 的做法,但是我还不会. 题意简述: 给定一棵 \(n\)(\(2\le n\le 10^5\))个点的树,边有边权. 定义一条路径 ...
- 「GM_脚本」获取 GitHub 项目文件的 jsDelivr CDN 地址「好像没啥用系列」
基本信息: name:「 GitHub 」获取文件的 jsDelivr 地址 desc:获取项目文件的 CDN 地址 url: https://github.com/wdssmq/userscript ...
- loj#2312. 「HAOI2017」八纵八横(线性基 线段树分治)
题意 题目链接 Sol 线性基+线段树分治板子题.. 调起来有点自闭.. #include<bits/stdc++.h> #define fi first #define se secon ...
随机推荐
- 【dfs】Sequence Decoding
Sequence Decoding 题目描述 The amino acids in proteins are classified into two types of elements, hydrop ...
- js 中加减乘除 比较精确的算法,js本身有些运算会出错,这里给出较精确的算法
问题这样的: 37.5*5.5=206.08 (JS算出来是这样的一个结果,我四舍五入取两位小数) 我先怀疑是四舍五入的问题,就直接用JS算了一个结果为:206.08499999999998 怎么会 ...
- JVM性能优化--类加载器,手动实现类的热加载
一.类加载的机制的层次结构 每个编写的".java"拓展名类文件都存储着需要执行的程序逻辑,这些".java"文件经过Java编译器编译成拓展名为". ...
- textarea与标签组合,点击标签填入标签内容,再次点击删除内容(vue)
需求:将textarea与span标签组合,点击标签自动填入标签文本内容,再次点击删除标签文本对应内容 原理:点击标签时,将标签内容作为参数,将内容拼接在textarea的value后面,再次点击标签 ...
- SAP Marketing Cloud功能简述(三) 营销活动内容设计和产品推荐
Grace的前两篇文章: SAP Marketing Cloud功能简述(一) : Contacts和Profiles SAP Marketing Cloud功能简述(二) : Target Grou ...
- node.js 调用mysql 数据库
1.在package.json中添加mysql依赖 命令:npm install mysql --save 2.项目中添加mysql文件夹 > 文件夹下创建config文件夹,并在config下 ...
- 【SpringMVC】Validation校验
一.概述 二.步骤 2.1 引入 Hibernate Validator 2.2 配置 2.3 创建CustomValidationMessages 2.4 校验规则 2.5 捕获错误 2.6 在页面 ...
- Shodan全世界在线设备搜索引擎
reproduction from https://danielmiessler.com/study/shodan/ What is Shodan? Shodan is a search engine ...
- Android笔记(十二)AndroidManiFest.xml
AndroidManiFest.xml清单文件是每个Android项目所必须的,它是整个Android应用的全局描述文件.AndroidManiFest.xml清单文件说明了该应用的名称.所使用的图标 ...
- 现在记录几款好用的vscode的插件
vscode 插件 Rainbow Brackets编码过程中,尤其在我们使用js进行函数式编程时,代码里会有很多的花括号,想要保证它们对称十分困难,所以就出现了上面小粉同学的尴尬局面,相信很多人都遇 ...