给定一棵 \(n\) 个点的带点权的树,求树上的路径 \(x_1,...,x_k\) ,最大化 \(\sum_{i=1}^k ia_{x_i}\)

Solution

树上路径问题可用点分治。

考虑如何合并两条路径对每条路径,记 \(l\) 为长度(点数),\(v\) 为 \(\sum_{i=1}^l ia_{x_i}\) ,\(s\) 为 \(\sum a_i\) ,那么对于两条路径 \((l_1,v_1,s_1),(l_2,v_2,s_2)\),它们的并为 \((l_1+l_2-1,v_1+s_1l_2+v_2,s_1+s_2)\)

于是答案为 \(v_1 + v_2 + s_1l_2\),其中 \(_1\) 是和之前扫描过的有关的信息,\(_2\) 是和当前正在扫描的位置有关的信息

(之所以这样取的原因是,之后李超线段树中我们需要控制下标的区间,而 \(l\) 的范围是较小的)

对于某一次查询来说,\(v_2\) 是定值,于是查询只有一个参数 \(l_2\) ,所以现在我们要最大化 \(l_2s_1 + v_1\),不妨转化为

  • 插入一条直线 \(y=ax+b\),其中 \(a=s_1,b=v_1\)
  • 询问 \(x=l_2\) 与所有直线交点中最上面的那一个

标准的李超线段树模板

我又把点分敲错,数组开小

#include <bits/stdc++.h>
using namespace std;
#define int long long const bool dbg = 0; namespace seg {
const int N = 4e6+5;
#define lc x<<1
#define rc x<<1|1
#define mid ((l+r)>>1)
struct Node {
int a,b;
} tree[N];
void ins(int x,int l,int r,int a,int b) {
if(tree[x].a*l+tree[x].b<=a*l+b&&tree[x].a*r+tree[x].b<=a*r+b) {
tree[x].a=a,tree[x].b=b;
}
else {
if(tree[x].a*l+tree[x].b>=a*l+b&&tree[x].a*r+tree[x].b>=a*r+b) return;
if(tree[x].a*mid+tree[x].b<a*mid+b)swap(tree[x].a,a),swap(tree[x].b,b);
if(a<tree[x].a)ins(lc,l,mid,a,b);
else ins(rc,mid+1,r,a,b);
}
}
void build(int x,int l,int r) {
tree[x].a=tree[x].b=0;
if(l==r)return;
build(lc,l,mid);
build(rc,mid+1,r);
}
int query(int x,int l,int r,int p) {
int ans=tree[x].a*p+tree[x].b;
if(l==r)return ans;
if(p<=mid)ans=max(ans,query(lc,l,mid,p));
else ans=max(ans,query(rc,mid+1,r,p));
return ans;
}
}; int tot;
const int N = 300005;
int n,val[N],siz[N],vis[N],mx[N],fa[N],l[N],v[N],s[N],sz,ans;
vector <int> g[N],sta; void dfs(int p) {
siz[p]=1;
mx[p]=0; //!!!
sta.push_back(p);
for(int q:g[p]) if(fa[p]!=q && !vis[q]) {
fa[q]=p;
dfs(q);
siz[p]+=siz[q];
mx[p]=max(mx[p],siz[q]);
}
} void dfs1(int p) {
if(dbg) cout<<" dfs1 "<<p<<" "<<l[p]<<" "<<v[p]<<" "<<s[p]<<endl;
seg::ins(1,1,sz,s[p],v[p]);
for(int q:g[p]) if(fa[p]!=q && !vis[q]) {
l[q]=l[p]+1;
s[q]=s[p]+val[q];
v[q]=v[p]+l[q]*val[q];
dfs1(q);
}
} void dfs2(int p) {
if(dbg) cout<<" dfs2 "<<p<<" "<<l[p]<<" "<<v[p]<<" "<<s[p]<<endl;
ans=max(ans,seg::query(1,1,sz,l[p])+v[p]);
if(dbg) cout<<" ans="<<ans<<endl;
for(int q:g[p]) if(fa[p]!=q && !vis[q]) {
l[q]=l[p]+1;
s[q]=s[p]+val[q];
v[q]=v[p]+s[q];
dfs2(q);
}
} void solve(int p) {
fa[p]=0;
sta.clear();
dfs(p);
for(int q:sta) mx[q]=max(mx[q],(int)sta.size()-siz[q]);
int r=0,mv=1e9;
for(int q:sta) if(mv>mx[q]) r=q, mv=mx[q];
vis[r]=1;
for(int q:sta) fa[q]=0;
dfs(r); //!!!
if(dbg) cout<<"solve "<<r<<endl;
sz=sta.size();
if(dbg) cout<<" round I"<<endl;
seg::build(1,1,sz);
l[r]=1; s[r]=val[r]; v[r]=val[r];
if(dbg) cout<<" "<<l[r]<<" "<<s[r]<<" "<<v[r]<<endl;
seg::ins(1,1,sz,s[r],v[r]);
for(int i=0;i<g[r].size();i++) {
int q=g[r][i];
if(vis[q]) continue;
if(dbg) cout<<" subtree "<<q<<endl;
l[q]=1;
v[q]=val[q];
s[q]=val[q];
dfs2(q);
l[q]=2;
v[q]=val[r]+val[q]*2;
s[q]=val[r]+val[q];
dfs1(q);
}
ans=max(ans,seg::query(1,1,sz,0));
if(dbg) cout<<" ans="<<ans<<endl;
seg::build(1,1,sz);
if(dbg) cout<<" round II"<<endl;
for(int i=g[r].size()-1;i>=0;--i) {
int q=g[r][i];
if(vis[q]) continue;
if(dbg) cout<<" subtree "<<q<<endl;
l[q]=1;
v[q]=val[q];
s[q]=val[q];
dfs2(q);
l[q]=2;
v[q]=val[r]+val[q]*2;
s[q]=val[r]+val[q];
dfs1(q);
}
for(int q:g[r]) if(!vis[q]) solve(q);
} signed main() {
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
//ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<n;i++) {
int t1,t2;
cin>>t1>>t2;
g[t1].push_back(t2);
g[t2].push_back(t1);
}
for(int i=1;i<=n;i++) cin>>val[i];
solve(1);
cout<<ans<<endl;
}

一杯茶一包烟,一个破题调一天

[CF1303G] Sum of Prefix Sums - 点分治,李超线段树的更多相关文章

  1. CF1303G Sum of Prefix Sums

    点分治+李超树 因为题目要求的是树上所有路径,所以用点分治维护 因为在点分治的过程中相当于将树上经过当前$root$的一条路径分成了两段 那么先考虑如何计算两个数组合并后的答案 记数组$a$,$b$, ...

  2. Codeforces 1303G - Sum of Prefix Sums(李超线段树+点分治)

    Codeforces 题面传送门 & 洛谷题面传送门 个人感觉这题称不上毒瘤. 首先看到选一条路径之类的字眼可以轻松想到点分治,也就是我们每次取原树的重心 \(r\) 并将路径分为经过重心和不 ...

  3. Codechef TSUM2 Sum on Tree 点分治、李超线段树

    传送门 点分治模板题都不会迟早要完 发现这道题需要统计所有路径的信息,考虑点分治统计路径信息. 点分治之后,因为路径是有向的,所以对于每一条路径都有向上和向下的两种.那么如果一条向上的路径,点数为\( ...

  4. Codeforces Round #463 F. Escape Through Leaf (李超线段树合并)

    听说正解是啥 set启发式合并+维护凸包+二分 根本不会啊 , 只会 李超线段树合并 啦 ... 题意 给你一颗有 \(n\) 个点的树 , 每个节点有两个权值 \(a_i, b_i\) . 从 \( ...

  5. Codeforces 1175G Yet Another Partiton Problem [DP,李超线段树]

    Codeforces 思路 首先吐槽一句:partiton是个什么东西?我好像在百度翻译里面搜不到呀qwq 发现不了什么性质,那就直接上DP吧.注意到DP可以分层,所以设\(dp_i\)表示当前层,分 ...

  6. 李超线段树(segment[HEOI2013]-洛谷T4097)

    (neng了好久好久才糊弄懂得知识点...) 一.李超线段树 在线动态维护一个二维平面直角坐标系, 支持插入一条线段, 询问与直线x = x0相交的所有线段中,交点y的最大/小值 (若有多条线段符合条 ...

  7. Codeforces 1175G - Yet Another Partiton Problem(李超线段树)

    Codeforces 题面传送门 & 洛谷题面传送门 这是一道李超线段树的毒瘤题. 首先我们可以想到一个非常 trivial 的 DP:\(dp_{i,j}\)​ 表示前 \(i\)​ 个数划 ...

  8. 【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交

    4515: [Sdoi2016]游戏 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 304  Solved: 129[Submit][Status][ ...

  9. 【BZOJ-3165】Segment 李超线段树(标记永久化)

    3165: [Heoi2013]Segment Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 368  Solved: 148[Submit][Sta ...

随机推荐

  1. qt creator源码全方面分析(2-10-3)

    目录 Plugin Meta Data 主键 插件描述键 依赖 可选依赖项 测试依赖项 命令行参数 Test.json示例 插件版本说明 Plugin Meta Data 插件的元数据文件是一个JSO ...

  2. 07-SpringMVC01

    今日知识 1. SpringMVC入门 2. SpringMVC的注解开发 SpringMVC入门 1. 简介: * Spring MVC是Spring提供的一个强大而灵活的web框架.借助于注解,S ...

  3. 再次小结windows服务的编写

    2013-03-23 21:05 (分类:计算机程序) 其实很简单 void mian() { //服务的分派表    SERVICE_TABLE_ENTRY DispatchTable[] ={   ...

  4. [Redis-CentOS7]Redis字符串操作(二)

    登录Redis # redis-cli 127.0.0.1:6379> 添加字符串 EX 超期时间60s 127.0.0.1:6379> set username 'leoshi' OK ...

  5. 动态规划------背包问题(c语言)

    /*背包问题: 背包所能容纳重量为10:共五件商品,商品重量用数组m存储m[5]={2,2,6,5,4}, 每件商品的价值用数组n存储,n[5]={6,3,5,4,6};求背包所能装物品的最大价值. ...

  6. 【C#】写文件时如何去掉编码前缀

    我们都知道,文件有不同的编码,例如我们常用的中文编码有:UTF8.GK2312 等. Windows 操作系统中,新建的文件会在起始部分加入几个字符的前缀,来识别编码. 例如,新建文本文件,写入单词 ...

  7. JavaScript中基本数据类型之间的转换

    在JavaScript中共有六种数据类型,其中有五种是基本数据类型,还有一种则是引用数据类型.五种基本数据类型分别是:Number 数值类型.String 字符串类型.Boolean 布尔类型, nu ...

  8. 移动端rem.js

    rem 只与 html 的 font-size 有关,比如html{font-size: 16px} body{font-size: 62.5%},那么 1rem 还是 16px,与其他无关 在头部引 ...

  9. Javaweb项目中修改表单时数据回显方法

    1.前言 先来说下什么是数据回显,比如我要修改我的个人信息,点击修改按钮后进入修改界面,在这个界面中直接将原来的信息显示在表单中,而不是空表单,这就是数据回显 2.思路 当点击修改的时候,从数据库中查 ...

  10. Bilibili手机端下载的Download文件批量转换为MP4软件【Bilibili_DownVideoToMp4】原创发布

    Bilibili手机端下载的Download文件批量转换为MP4软件[Bilibili_DownVideoToMp4]原创发布 起因 Bilibili手机端的视频下载下来只能在手机上看,手机屏幕太小看 ...