传送门

既然是树上路径统计问题,不难想到要使用树分治,这里以点分治为例

由点分治的性质,每层只需要考虑经过重心的路径

因为需要维护路径长度在一定范围内的最大权值和,所以要用一个数据结构维护一下到根节点距离在一定范围内的最大权值和

显然线段树是一个不错的选择,对每个子树建立一个线段树,根节点的答案用每个子树的线段树都更新一遍即可

考虑更新子树中的点的答案,这时需要使用除这棵子树外的所有子树的线段树一起更新

我们可以使用线段树合并来维护,给子树任意确定一个顺序,然后通过维护每个子树的前缀和后缀线段树的并即可快速得到除去某棵子树后的线段树

显然复杂度是有保证的,因为线段树合并的复杂度无论如何都不会高于暴力插入三遍

总复杂度\(O(n\log^2 n)\),常数应该不小,不过跑的挺快233333

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005,maxm=maxn*100;
const long long INF=0x5f5f5f5f5f5f5f5fll;
void solve(int,int);
int getcenter(int,int);
int getdis(int);
void getans(int,int);
void modify(int,int,int&);
int merge(int,int);
long long query(int,int,int);
long long mx[maxm];
int lc[maxm],rc[maxm],cnt=0,root[maxn],prefix[maxn],suffix[maxn];
vector<int>G[maxn];
bool vis[maxn];
long long w[maxn],ans[maxn],ant[maxn],tmp;
int p[maxn],size[maxn],son[maxn],q[maxn],d[maxn],pr[maxn],nx[maxn];
int n,m,L,R,val[maxn],s,t;
int main(){
mx[0]=-INF;
scanf("%d%d%d",&n,&L,&R);
for(int i=1;i<=n;i++){
scanf("%d",&val[i]);
ans[i]=-3472328296227680305ll;
}
for(int i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
solve(1,n);
for(int i=1;i<=n;i++){
if(i>1)printf(" ");
printf("%lld",ans[i]);
}
printf("\n");
return 0;
}
void solve(int x,int sz){
x=getcenter(x,sz);
m=sz;
vis[x]=true;
w[x]=val[x];
d[x]=0;
if(sz==1)return;
s=0;
tmp=w[x];
modify(0,m,root[x]);
for(int i=0;i<(int)G[x].size();i++)
if(!vis[G[x][i]]){
p[G[x][i]]=x;
getdis(G[x][i]);
}
s=L;t=R;
for(int i=0,last=0;i<(int)G[x].size();i++)
if(!vis[G[x][i]]){
if(s<=m)ans[x]=max(ans[x],query(0,m,root[G[x][i]]));
prefix[G[x][i]]=merge(prefix[last],root[G[x][i]]);
pr[G[x][i]]=last;
last=G[x][i];
}
ant[x]=-INF;
for(int i=(int)G[x].size()-1,last=0;~i;i--)
if(!vis[G[x][i]]){
suffix[G[x][i]]=merge(suffix[last],root[G[x][i]]);
nx[G[x][i]]=last;
last=G[x][i];
}
for(int i=0;i<(int)G[x].size();i++)
if(!vis[G[x][i]])getans(G[x][i],val[x]);
ans[x]=max(ans[x],ant[x]);
root[x]=0;
for(int i=0;i<(int)G[x].size();i++)
if(!vis[G[x][i]]){
pr[G[x][i]]=nx[G[x][i]]=0;
root[G[x][i]]=prefix[G[x][i]]=suffix[G[x][i]]=0;
}
cnt=0;
for(int i=0;i<(int)G[x].size();i++)
if(!vis[G[x][i]])solve(G[x][i],size[G[x][i]]);
}
int getcenter(int x,int s){
int head=0,tail=0;
q[tail++]=x;
while(head!=tail){
x=q[head++];
size[x]=1;
son[x]=0;
for(int i=0;i<(int)G[x].size();i++)
if(!vis[G[x][i]]&&G[x][i]!=p[x]){
p[G[x][i]]=x;
q[tail++]=G[x][i];
}
}
for(int i=tail-1;i;i--){
x=q[i];
size[p[x]]+=size[x];
if(size[x]>size[son[p[x]]])son[p[x]]=x;
}
x=q[0];
while(son[x]&&size[son[x]]>(s>>1))x=son[x];
return x;
}
int getdis(int x){
int head=0,tail=0,rt=x;
q[tail++]=x;
while(head!=tail){
x=q[head++];
s=d[x]=d[p[x]]+1;
tmp=w[x]=w[p[x]]+val[x];
modify(0,m,root[rt]);
size[x]=1;
for(int i=0;i<(int)G[x].size();i++)
if(!vis[G[x][i]]&&G[x][i]!=p[x]){
p[G[x][i]]=x;
q[tail++]=G[x][i];
}
}
for(int i=tail-1;i;i--){
x=q[i];
size[p[x]]+=size[x];
}
return d[q[tail-1]];
}
void getans(int x,int v){
int head=0,tail=0,rt=merge(prefix[pr[x]],suffix[nx[x]]);
q[tail++]=x;
while(head!=tail){
x=q[head++];
s=L-d[x];
t=R-d[x];
if(t<0||s>m)ant[x]=-INF;
else ant[x]=w[x]-v+query(0,m,rt);
if(s<=0&&t>=0)ant[x]=max(ant[x],w[x]);
for(int i=0;i<(int)G[x].size();i++)
if(!vis[G[x][i]]&&G[x][i]!=p[x]){
p[G[x][i]]=x;
q[tail++]=G[x][i];
}
}
for(int i=tail-1;~i;i--){
x=q[i];
ant[p[x]]=max(ant[p[x]],ant[x]);
ans[x]=max(ans[x],ant[x]);
}
}
void modify(int l,int r,int &rt){
if(!rt){
rt=++cnt;
mx[rt]=-INF;
lc[rt]=rc[rt]=0;
}
mx[rt]=max(mx[rt],tmp);
if(l==r)return;
int mid=(l+r)>>1;
if(s<=mid)modify(l,mid,lc[rt]);
else modify(mid+1,r,rc[rt]);
}
int merge(int x,int y){
if(!x||!y)return x|y;
int z=++cnt;
mx[z]=max(mx[x],mx[y]);
lc[z]=merge(lc[x],lc[y]);
rc[z]=merge(rc[x],rc[y]);
return z;
}
long long query(int l,int r,int rt){
if(s<=l&&t>=r)return mx[rt];
int mid=(l+r)>>1;
if(t<=mid)return query(l,mid,lc[rt]);
if(s>mid)return query(mid+1,r,rc[rt]);
return max(query(l,mid,lc[rt]),query(mid+1,r,rc[rt]));
}

LOJ#6463 AK YOI 树分治+线段树合并的更多相关文章

  1. UVALive 7148 LRIP【树分治+线段树】

    题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以 ...

  2. 【BZOJ4372】烁烁的游戏 动态树分治+线段树

    [BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距 ...

  3. LOJ 6145 Easy (动态点分治+线段树)

    题目传送门 先建出来点分树,以每个点为根开线段树,维护点分子树内编号为$[l,r]$的儿子到根的距离最小值 每次查询$x$开始,沿着点分树向上跑,在每个点的线段树的$[l,r]$区间里都查一遍取$mi ...

  4. 【BZOJ3730】震波 动态树分治+线段树

    [BZOJ3730]震波 Description 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土 ...

  5. 7.18 NOI模拟赛 因懒无名 线段树分治 线段树维护直径

    LINK:因懒无名 20分显然有\(n\cdot q\)的暴力. 还有20分 每次只询问一种颜色的直径不过带修改. 容易想到利用线段树维护直径就可以解决了. 当然也可以进行线段树分治 每种颜色存一下直 ...

  6. bzoj3730 [震波][动态树分治+线段树+LCA]

    震波 Time Limit: 15 Sec  Memory Limit: 256 MBSubmit: 1573  Solved: 358[Submit][Status][Discuss] Descri ...

  7. LOJ.121.[离线可过]动态图连通性(线段树分治 按秩合并)

    题目链接 以时间为下标建线段树.线段树每个节点开个vector. 对每条边在其出现时间内加入线段树,即,把这条边按时间放在线段树的对应区间上,会影响\(O(\log n)\)个节点. 询问就放在线段树 ...

  8. BZOJ4317Atm的树&BZOJ2051A Problem For Fun&BZOJ2117[2010国家集训队]Crash的旅游计划——二分答案+动态点分治(点分树套线段树/点分树+vector)

    题目描述 Atm有一段时间在虐qtree的题目,于是,他满脑子都是tree,tree,tree…… 于是,一天晚上他梦到自己被关在了一个有根树中,每条路径都有边权,一个神秘的声音告诉他,每个点到其他的 ...

  9. 【loj6145】「2017 山东三轮集训 Day7」Easy 动态点分治+线段树

    题目描述 给你一棵 $n$ 个点的树,边有边权.$m$ 次询问,每次给出 $l$ .$r$ .$x$ ,求 $\text{Min}_{i=l}^r\text{dis}(i,x)$ . $n,m\le ...

随机推荐

  1. ajax post 400 bad request

    是前端ajax没有加声明:contentType:'application/json',

  2. Redis的Pub/Sub机制存在的问题以及解决方案

    Redis的Pub/Sub机制使用非常简单的方式实现了观察者模式,但是在使用过程中我们发现,它仅仅是实现了发布订阅机制,但是很多的场景没有考虑到.例如一下的几种场景: 1.数据可靠性无法保证 一个re ...

  3. 【vim】分割窗口、标签页与Quickfix窗口

    vim支持窗口分割和标签页,合适地使用这两种特性可以使文字编辑工作更愉快. 1. 窗口分割 vim支持窗口的水平分割和垂直分割.以下是常用的操作指令或快捷键. 命令 说明 vim -o <fil ...

  4. day 53 Django form 组件 autu 组件

    https://github.com/Endless-Clould/homework/tree/master/auto_zujian ---恢复内容开始--- 前情提要: Django 组件 的 fo ...

  5. php判断是否使用手机访问

    直接上代码 /** * 检测是否使用手机访问 * @access public * @return bool */ public function isMobile() { if (isset($_S ...

  6. logcat -- 基本用法

    1.Log类是一个日志类,我们可以在代码中使用logcat打印出消息 常见的日志记录方法有: v(String,String)  --verbose 显示全部信息 d(String,String) - ...

  7. js04

    接着看一些js的基础,这里主要说一下js的对象. 1.对象:    js中的所有事物都可以看作是对象:字符串.数值.数组.函数...    内建对象:String    Date    Array   ...

  8. error 'there is already an open datareader associated with this command which must be closed first'

    This can be easily solved by allowing MARS in your connection string. Add MultipleActiveResultSets=t ...

  9. 利用Windows资源监视器解决文件夹无法改名无法删除问题

    在win7等Windows系统操作文件夹更名.删除时经常会报错,操作无法完成,balabala 这个时候仅凭在用的软件去一个一个找是很难的,即便软件全关了,还有后台进程,,,奔溃吧 好了,现在我们有了 ...

  10. 【原】Ubuntu13.04安装、卸载Gnome3.8

    添加 GNOME 3 PPA(Personal Package Archives) 我们先给Ubuntu的Software Sources增加GNOME 3 PPA.这可以不用命令行就做到,但出于简单 ...