「CF150E」Freezing with Style「点分治」「单调队列」
题意
给定一颗带边权的树,求一条边数在、之间的路径,并使得路径上边权的中位数最大。输出一条可行路径的两个端点。这里若有偶数个数,中位数为中间靠右的那个。
题解
看一眼是点分。然后发现中位数要二分,把的权值设为,的设为,问题转换为找边权的路径
易发现一个子树,每个深度存一个最大值就行
考虑怎么合并两个子树:假设之前子树答案是,表示深度为的边权最大和。当前为。
再考虑的时候,能与他配对的实际上是区间到,并且这个区间随着增大而减小
用单调队列维护最大值即可。
注意子树要按深度从小到大排序,不然复杂度会被卡成平方!
时间复杂度。实现注意细节。
#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;
}
本文作者:hfhongzy
本文链接:https://www.cnblogs.com/hongzy/p/11733688.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· 本地部署 DeepSeek:小白也能轻松搞定!
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 从 Windows Forms 到微服务的经验教训
· 李飞飞的50美金比肩DeepSeek把CEO忽悠瘸了,倒霉的却是程序员
· 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee