传送门


只会线段树……关于单调队列的解法可以去看“重建计划”一题。

看到路径长度$\in [L,R]$考虑点分治。可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边)颜色相同的两条路径在拼合的时候在加上两条路径的权值之后需要减掉始边颜色的权值(因为被计算了两次),而初始边颜色不同的进行拼合就直接将两条路径的权值加起来即可。我们考虑分开维护这两种拼合。

在每一个分治中心里,我们对其引出的边按照颜色排序(目的是使得始边颜色相同的若干子树放在一起统一遍历),维护两个线段树,一个维护之前遍历过的子树中初始边颜色与当前子树不同的子树中每种长度的路径的最大权值,另一个维护之前遍历过的子树中初始边颜色与当前子树相同的子树中每种长度的路径的最大权值。

那么我们每一次遍历完一棵子树,在线段树中对于每一种长度的路径在线段树上查询可以拼合的长度的路径的最大权值,记得在第二个线段树中询问到的答案需要减去始边颜色的权值,然后用这一棵子树的路径更新第二个线段树。每一次遍历完一种颜色就把第二个线段树合并到第一个线段树内即可。复杂度$O(nlog^2n)$

 #include<bits/stdc++.h>
#define INF 0x7fffffff
#define int long long
#define mid ((l + r) >> 1)
//This code is written by Itst
using namespace std; inline int read(){
int a = ;
bool f = ;
char c = getchar();
while(c != EOF && !isdigit(c)){
if(c == '-')
f = ;
c = getchar();
}
while(c != EOF && isdigit(c)){
a = (a << ) + (a << ) + (c ^ '');
c = getchar();
}
return f ? -a : a;
} const int MAXN = ;
vector < pair < int , int > > Edge[MAXN];
struct node{
int l , r , maxN;
}Tree[MAXN * ];
int N , M , L , R , ans , cntNode , nowSize , minSize , minInd , roota , rootb;
int val[MAXN] , size[MAXN] , sz[MAXN] , mx[MAXN];
bool vis[MAXN]; inline int allocate(){
int t = ++cntNode;
Tree[t].l = Tree[t].r = ;
Tree[t].maxN = -INF;
return t;
} inline void pushup(int x){
Tree[x].maxN = max(Tree[Tree[x].l].maxN , Tree[Tree[x].r].maxN);
} inline int max(int a , int b){
return a > b ? a : b;
} int merge(int p , int q){
if(!p)
return q;
if(!q)
return p;
Tree[p].maxN = max(Tree[p].maxN , Tree[q].maxN);
Tree[p].l = merge(Tree[p].l , Tree[q].l);
Tree[p].r = merge(Tree[p].r , Tree[q].r);
return p;
} void insert(int& now , int l , int r , int tar , int num){
if(!now)
now = allocate();
if(l == r)
Tree[now].maxN = max(Tree[now].maxN , num);
else{
if(mid >= tar)
insert(Tree[now].l , l , mid , tar , num);
else
insert(Tree[now].r , mid + , r , tar , num);
pushup(now);
}
} int query(int now , int l , int r , int L , int R){
if(l >= L && r <= R)
return Tree[now].maxN;
if(!now)
return -INF;
int maxN = -INF;
if(mid >= L)
maxN = max(maxN , query(Tree[now].l , l , mid , L , R));
if(mid < R)
maxN = max(maxN , query(Tree[now].r , mid + , r , L , R));
return maxN;
} void getSize(int x){
vis[x] = ;
++nowSize;
for(int i = ; i < sz[x] ; ++i){
int t = Edge[x][i].second;
if(!vis[t])
getSize(t);
}
vis[x] = ;
} void getRoot(int x){
size[x] = vis[x] = ;
int maxN = ;
for(int i = ; i < sz[x] ; ++i){
int t = Edge[x][i].second;
if(!vis[t]){
getRoot(t);
size[x] += size[t];
maxN = max(maxN , size[t]);
}
}
maxN = max(maxN , nowSize - size[x]);
if(maxN < minSize){
minSize = maxN;
minInd = x;
}
vis[x] = ;
} void dfs(int x , int c , int len , int nowCol){
if(len > R)
return;
vis[x] = ;
mx[len] = max(mx[len] , c);
for(int i = ; i < sz[x] ; ++i){
int t = Edge[x][i].second;
if(!vis[t])
dfs(Edge[x][i].second , nowCol == Edge[x][i].first ? c : c + val[Edge[x][i].first] , len + , Edge[x][i].first);
}
vis[x] = ;
} void solve(int x){
nowSize = cntNode = roota = rootb = ;
minSize = INF;
getSize(x);
getRoot(x);
int root = minInd;
getSize(root);
vis[root] = ;
for(int i = ; i < sz[root] ; ++i){
int t = Edge[root][i].second , r = Edge[root][i].first;
if(!vis[t]){
memset(mx , -0x3f , sizeof(int) * (size[t] + ));
if(rootb && r != Edge[root][i - ].first){
roota = merge(roota , rootb);
rootb = ;
}
dfs(t , val[r] , , r);
for(int i = ; i <= size[t] && i < R && mx[i] != mx[] ; ++i)
ans = max(max(ans , i >= L && i <= R ? mx[i] : -INF) , max(query(roota , , R , max( , L - i) , R - i) , query(rootb , , R , max( , L - i) , R - i) - val[r]) + mx[i]);
ans = max(ans , mx[R]);
for(int i = ; i <= size[t] && i < R && mx[i] != mx[] ; ++i)
insert(rootb , , R , i , mx[i]);
}
}
for(int i = ; i < sz[root] ; ++i){
int t = Edge[root][i].second;
if(!vis[t])
solve(t);
}
} signed main(){
#ifndef ONLINE_JUDGE
freopen("3714.in" , "r" , stdin);
//freopen("3714.out" , "w" , stdout);
#endif
Tree[].maxN = ans = -INF;
N = read();
M = read();
L = read();
R = read();
for(int i = ; i <= M ; ++i)
val[i] = read();
for(int i = ; i < N ; ++i){
int a = read() , b = read() , c = read();
Edge[a].push_back(make_pair(c , b));
Edge[b].push_back(make_pair(c , a));
++sz[a];
++sz[b];
}
for(int i = ; i <= N ; ++i)
sort(Edge[i].begin() , Edge[i].end());
solve();
cout << ans;
return ;
}

BZOJ4860 BJOI2017 树的难题 点分治、线段树合并的更多相关文章

  1. [BJOI2017]树的难题 点分治 线段树

    题面 [BJOI2017]树的难题 题解 考虑点分治. 对于每个点,将所有边按照颜色排序. 那么只需要考虑如何合并2条链. 有2种情况. 合并路径的接口处2条路径颜色不同 合并路径的接口处2条路径颜色 ...

  2. P3714 [BJOI2017]树的难题 点分治+线段树合并

    题目描述 题目传送门 分析 路径问题考虑点分治 对于一个分治中心,我们可以很容易地得到从它开始的一条路径的价值和长度 问题就是如何将不同的路径合并 很显然,对于同一个子树中的所有路径,它们起始的颜色是 ...

  3. [BJOI2017]树的难题 点分治,线段树合并

    [BJOI2017]树的难题 LG传送门 点分治+线段树合并. 我不会写单调队列,所以就写了好写的线段树. 考虑对于每一个分治中心,把出边按颜色排序,这样就能把颜色相同的子树放在一起处理.用一棵动态开 ...

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

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

  5. BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector

    题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...

  6. LOJ#6463 AK YOI 树分治+线段树合并

    传送门 既然是树上路径统计问题,不难想到要使用树分治,这里以点分治为例 由点分治的性质,每层只需要考虑经过重心的路径 因为需要维护路径长度在一定范围内的最大权值和,所以要用一个数据结构维护一下到根节点 ...

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

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

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

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

  9. 【bzoj4372】烁烁的游戏 动态点分治+线段树

    题目描述 给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. 输入 第一行两个正整数:n,m接下来的n-1 ...

  10. 【bzoj3730】震波 动态点分治+线段树

    题目描述 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土地常常发生地震,并且随着时代的发展,城市 ...

随机推荐

  1. Apex 的异常处理

    Apex 中的异常处理 在 Apex 中,和其他语言类似,对于异常处理通常使用 try.catch.finally.throw 等关键字. 对于每一个 try 代码段,必须要有至少一个 catch 或 ...

  2. 传统BI还是自助式BI---BI与数据分析 ZT

    自助式BI或者自助式数据分析是最近几年兴起的一个概念.根据Gartner发布的信息,Self Service Business Intelligence(SSBI)被定义为“终端用户在被批准和支持的平 ...

  3. IOS7如何获取设备唯一标识

    WWDC 2013已经闭幕,IOS7 Beta随即发布,界面之难看无以言表...,简直就是山寨Android. 更让IOS程序猿悲催的是,设备唯一标识的MAC Address在IOS7中也失效了. I ...

  4. Kotlin入门(18)利用单例对象获取时间

    前面介绍了,使用扩展函数可以很方便地扩充数组Array的处理功能,例如交换两个数组元素.求数组的最大元素等等.那么除了数组之外,日期和时间的相关操作,也是很常见的,比如获取当前日期,获取当前时间.获取 ...

  5. (网页)js常见报错之Unexpected token in JSON at position

    出现这个报错提示,根本原因只有一个--json解析异常,所以请大家直接去关注自己json的返回数据注意检查其返回内容和内容的格式是否正确,至于本文血案的导火索是因为json注释滴问题.

  6. 机器学习实战(Machine Learning in Action)学习笔记————10.奇异值分解(SVD)原理、基于协同过滤的推荐引擎、数据降维

    关键字:SVD.奇异值分解.降维.基于协同过滤的推荐引擎作者:米仓山下时间:2018-11-3机器学习实战(Machine Learning in Action,@author: Peter Harr ...

  7. C语言开发的思考

    维护过十万行代码的通信协议,自己从头开始开发过几万行的代码,步骤: 1.移植性.为移植性对数据类型做重新定义. 2.内存计数.不要直接使用malloc和free,而是给所有类型的内存申请定义类型,并计 ...

  8. SQL alwayson 辅助接点查询统计信息“丢失”导致查询失败

    ALWAYSON 出现以下情况已经2次了,记录下: DBCC 执行完毕.如果 DBCC 输出了错误信息,请与系统管理员联系. 消息 2767,级别 16,状态 1,过程 sp_table_statis ...

  9. 系统运维|IIS的日志设置

    摘要: 1.服务器告警,磁盘资源不足 2.检查发现是IIS日志没有清理并且设置有误.在E盘占用了200G的空间 3.原则上IIS日志不能放在C盘,避免C盘写满了导致操作系统异常 4.附上IIS日志按天 ...

  10. logstash之input、codec学习

    Logstash最强大的功能在于丰富的过滤器插件.此过滤器提供的并不单单是过滤的功能,还可以对进入过滤器的原始数据进行复杂的逻辑处理.甚至添加独特的事件到后续流程中. 1.logstash基本语法组成 ...