BZOJ4860 BJOI2017 树的难题 点分治、线段树合并
只会线段树……关于单调队列的解法可以去看“重建计划”一题。
看到路径长度$\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 树的难题 点分治、线段树合并的更多相关文章
- [BJOI2017]树的难题 点分治 线段树
		
题面 [BJOI2017]树的难题 题解 考虑点分治. 对于每个点,将所有边按照颜色排序. 那么只需要考虑如何合并2条链. 有2种情况. 合并路径的接口处2条路径颜色不同 合并路径的接口处2条路径颜色 ...
 - P3714 [BJOI2017]树的难题 点分治+线段树合并
		
题目描述 题目传送门 分析 路径问题考虑点分治 对于一个分治中心,我们可以很容易地得到从它开始的一条路径的价值和长度 问题就是如何将不同的路径合并 很显然,对于同一个子树中的所有路径,它们起始的颜色是 ...
 - [BJOI2017]树的难题 点分治,线段树合并
		
[BJOI2017]树的难题 LG传送门 点分治+线段树合并. 我不会写单调队列,所以就写了好写的线段树. 考虑对于每一个分治中心,把出边按颜色排序,这样就能把颜色相同的子树放在一起处理.用一棵动态开 ...
 - UVALive 7148 LRIP【树分治+线段树】
		
题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以 ...
 - BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector
		
题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...
 - LOJ#6463 AK YOI 树分治+线段树合并
		
传送门 既然是树上路径统计问题,不难想到要使用树分治,这里以点分治为例 由点分治的性质,每层只需要考虑经过重心的路径 因为需要维护路径长度在一定范围内的最大权值和,所以要用一个数据结构维护一下到根节点 ...
 - 【loj6145】「2017 山东三轮集训 Day7」Easy  动态点分治+线段树
		
题目描述 给你一棵 $n$ 个点的树,边有边权.$m$ 次询问,每次给出 $l$ .$r$ .$x$ ,求 $\text{Min}_{i=l}^r\text{dis}(i,x)$ . $n,m\le ...
 - 【BZOJ4372】烁烁的游戏 动态树分治+线段树
		
[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距 ...
 - 【bzoj4372】烁烁的游戏  动态点分治+线段树
		
题目描述 给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. 输入 第一行两个正整数:n,m接下来的n-1 ...
 - 【bzoj3730】震波  动态点分治+线段树
		
题目描述 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i].不幸的是,这片土地常常发生地震,并且随着时代的发展,城市 ...
 
随机推荐
- 【转】ASP.NET Core 依赖注入
			
DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET Core的DI实现以及对实例 ...
 - Salesforce的报表和仪表板
			
报表是现代企业中最常用到的功能之一.Salesforce中提供了强大的报表和仪表板功能. 报表和仪表板简介 报表是一组数据展示,用户可以自定义规则,只有符合相应规则的数据才会显示出来. Salesfo ...
 - 不要拿ERP的报表忽悠领导!——一个报表引发的企业经营反思
			
文 | 帆软数据应用研究院船长 本文出自:知乎专栏<帆软数据应用研究院>——数据干货&资讯集中地 领导的经营决策能只依赖于ERP报表吗? 不能! 1. ERP报表个性化不足:企业经 ...
 - 《Inside C#》笔记(二) 初识C#
			
一 程序的编译.构成 a) 编写C#代码一般用VS,但作者在这儿介绍了使用记事本编写C#代码并编译运行的过程,以便对VS有更深入的认识. 用记事本编写C#代码后,修改文本文件的后缀为.cs,然后用cs ...
 - id、name、setter方法注入、构造方法注入、工厂方法注入、注解注入、方法注入、方法替换、Web作用域、普通bean引用Web作用域的bean
			
spring IoC的id和name id的命名需要满足XML对id的命名规范,必须以字母开始,后面可以是字母.数字.连字符.下画线.句号.冒号等等号,但逗号和空格是非法的.如果用户确实希望用一些特殊 ...
 - C#委托之我见
			
委托的使用方式很简单,了解一下基本语法就可以开撸了.但是使用委托的真正难题是不知道应用场景,就像习得了一门新功夫,但是却找不到任何施展拳脚的地方.这个难题一直困然着我,直到最近仿佛有所领悟,所以赶紧记 ...
 - MySQL 8.0 —— 数据字典
			
1.简介 MySQL 8.0 将数据库元信息都存放于InnoDB存储引擎表中,在之前版本的MySQL中,数据字典不仅仅存放于特定的存储引擎表中,还存放于元数据文件.非事务性存储引擎表中.本文将会介绍M ...
 - C# 生成强命名程序集并添加到GAC
			
针对一些类库项目或用户控件项目(一般来说,这类项目最后编译生成的是一个或多个dll文件),在程序开发完成后,有时需要将开发的程序集(dll文件)安装部署到GAC(全局程序集缓存)中,以便其他的程序也可 ...
 - 【项目 · WonderLand】 系 统 设 计
			
团 队 作 业 ---- 系 统 设 计 Part 0 · 简 要 目 录 Part 1 · 完 善 需 求 规 格 说 明 书 Part 2 · 团 队 编 码 规 范 Part 3 · 数 据 库 ...
 - Beta阶段总结博客(麻瓜制造者)
			
Beta冲刺过程中各个成员的贡献百分比: 成员 贡献值 邓弘立 15% 符天愉 14% 江郑 14% 刘双玉 14% 肖小强 13% 李佳铭 11% 汪志彬 11% 伍杰麟 8% 项目的发布说明 本版 ...