传送门


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

看到路径长度$\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. 【读书笔记】iOS-iCloud文件备份

    iOS应用在运行时经常要创建一些文件,不过这些文件要如何存放呢?有没有什么要求呢? 由于手机资源空间有限而且考虑到Apple推出的iCloud,我们确实要对创建出的文件按照作用的不同,分出几种类别出来 ...

  2. Appium学习——Appium工作原理

    appium的工具原理 Appium-client>>>>Appium-server>>>>移动设备 ========================= ...

  3. SQL Server 2000详细安装过程及配置

    说明:这篇文章是几年前我发布在网易博客当中的原创文章,但由于网易博客现在要停止运营了,所以我就把这篇文章搬了过来,虽然现如今SQL Server 2000软件早已经过时了,但仍然有一部分人在使用它,尤 ...

  4. mybatis学习--缓存(一级和二级缓存)

    声明:学习摘要! MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级) ...

  5. asp.net根据参数找不到记录后响应404及显示错误页

    在asp.net mvc 中,action方法里根据参数获取数据,假如获取的数据为空,为了响应404错误页,我们可以return HttpNotFound(); 但是在asp.net webform中 ...

  6. python装饰器(新年第一写)

    祭奠碌碌无为的2018,想想其实也不算碌碌无为,至少我还搞懂了装饰器,写了一堆有用没用的玩意 原来觉得装饰器挺难的,直到2018年的最后几天,突然就明白了,难道这就是传说中的开天聪么 言归正传,之所以 ...

  7. Ubuntu 16.04 LTS 降级安装GCC 4.8

    转载自https://www.linuxidc.com/Linux/2017-03/142299.htm Ubuntu 16.04 LTS 降级安装GCC 4.8 [日期:2017-03-28] 来源 ...

  8. IntelliJ IDEA 项目结构旁边出现 0%classes,0% lines covered

    不知道一不小心点到哪里,项目变成如下形式 使用ctrl +  Alt + F6弹出如下框,取消勾选-->点击Show Selected就可以去掉了 官网解释

  9. 5分钟入门Tornado

    Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效 ...

  10. Bootstrap收尾

    一  响应式布局 二  Bootstrap补充 三 常用插件 一  响应式布局 响应式介绍 - 响应式布局是什么? 同一个网页在不同的终端上呈现不同的布局等 - 响应式怎么实现的? 1. CSS3 m ...