传送门


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

看到路径长度$\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. css iphonex适配

    /*  iphonex适配 */ @media only screen and (device-width:375px) and (-webkit-device-pixel-ratio: 3) { . ...

  2. SuperMap 三维产品资料一览表

    转自:http://blog.csdn.net/supermapsupport/article/details/68924713 如何能快速地开发项目中的三维功能呢?本文为您提供全方位的三维资料,为您 ...

  3. SQLServer 2005Windows验证如何改为混合模式验证

    SQL Server 2005 Windows验证如何改为混合模式验证[摘] by:授客 QQ:1033553122 默认情况下,SQL Server 2005 Express是采用集成的Window ...

  4. JS代码段:返回yyyy-mm-dd hh:mm:ss

    最近做项目的时候正好用到,本着能抄就抄的心态去百度搜索现成的代码, 没想到抄下来的好几个都是错的,要么getMonth没有加1,要么10以下的数字前面没有加0, 我真是日了狗了,这次把写好的正确的直接 ...

  5. oracle count函数

    用来返回查询的行数. 当指定distinct时,不能接order_by_clause: 如果指定表达式,count返回表达式不为空的值: 当指定*号时,它返回所有行,含重复行和空值.count从不返回 ...

  6. VS2015安装与C++进行简单单元测试

    1:VS2015是微软最新发布的编译器,http://www.itellyou.cn/这是我们的北航大神助教提供的下载网址,以前我们都是自己在网上找,找到的总不是那么如意,这下大神助教提供的网址就好好 ...

  7. 学生与部门管理app-产品功能与界面的简单设计

    学生与部门管理app-产品功能与界面的简单设计 1. 结对成员学号 我:********* 大佬:*******10 2. 需求分析(NABCD模型) 2.1 N-需求 各个部门在开学初占据学校青春广 ...

  8. 【BZOJ4946】[NOI2017]蔬菜(贪心)

    [BZOJ4946][NOI2017]蔬菜(贪心) 题面 BZOJ 洛谷 UOJ 题解 忽然发现今年\(NOI\)之前的时候切往年\(NOI\)的题目,就\(2017\)年的根本不知道怎么下手(一定是 ...

  9. 装饰器 以及 django 中的应用

    装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权 ...

  10. 从头学Android之RelativeLayout相对布局

    http://blog.csdn.net/worker90/article/details/6893246 相对布局对于做Web开发来说再熟悉不过了,我们在用CSS+DIV的时候经常会用到这些类似的相 ...