传送门


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

看到路径长度$\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. IDEA项目搭建七——使用Feign简化消费者端操作

    一.简介 我们可以看到上一篇文章的消费者这边调用Service时比较麻烦,所以我们可以使用Feign来简化这部分操作,它底层也是使用Ribbon实现的只是Ribbon支持HTTP和TCP两种通信协议, ...

  2. 英雄无敌HoMM3-死亡阴影SOD-神之苏醒WOG-封神NABI-MOD等相关文件

    英雄无敌HoMM3:死亡阴影SOD 英雄无敌3之死亡阴影(Heroes of Might and Magic III: Shadow of Death,简记为HoMM III: SOD)发行于1999 ...

  3. (网页)人人都会的35个Jquery小技巧

    转自CSDN: 收集的35个 jQuery 小技巧/代码片段,可以帮你快速开发. 1. 禁止右键点击 $(document).ready(function(){ $(document).bind(&q ...

  4. LeetCode题解之 Subtree of Another Tree

    1.题目描述 2.问题分析 判断一个节点,然后判断子树. 3.代码 bool isSubtree(TreeNode* s, TreeNode* t) { if (s == NULL) return f ...

  5. oh-my-zsh安装与使用

    使用oh-my-zsh之前确保安装过zsh 通过脚本安装: sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussel ...

  6. plsql developer如何查询SQL语句执行历史记录(转)

    相信很多在plsql developer调试oracle的朋友,经常会遇到在plsql developer执行的某一条SQL语句没有保存,那么我们在plsql developer下如何找到我们执行过的 ...

  7. 【PAT】B1058 选择题(20 分)

    这道题的逻辑怪复杂的,写起来蛮费时间的 结构体中要储存的信息多,整体不难,信息量大,容易把人搞蒙 #include<stdio.h> #include<string.h> #i ...

  8. django项目中在settings中配置静态文件

    STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static'), ] 写成大写可能看不太懂,但是小写的意思非常明显:staticfiles_dir = [ o ...

  9. 学生&部门智能匹配程序

    Github链接 结对成员: 031502308 付逸豪 031502113 胡俊钦 数据生成 样例链接: 来点我啊 由于在生成数据的时候做了很多符合实际情况的限制,所以生成的数据都挺好的,就随便选了 ...

  10. SAP CRM 忠诚度相关表的关系图

    这是一张有关会员,积分,活动等内容的相关表的关系图,对相关的开发工作会有帮助. 原文标题:Table schema for managing customer loyality 本文链接:http:/ ...