题面

[BJOI2017]树的难题

题解

考虑点分治。

对于每个点,将所有边按照颜色排序。

那么只需要考虑如何合并2条链。

有2种情况。

  • 合并路径的接口处2条路径颜色不同
  • 合并路径的接口处2条路径颜色相同

我们分别考虑这2种情况。

维护2棵线段树,分别表示与当前接口颜色不同和颜色相同。

如果我们遍历完了一棵子树,就将这棵子树的答案加入到颜色相同的线段树里面。

如果我们遍历完了一段颜色,就将第2个线段树合并到第一个线段树里面。

当然更新答案要在上面2个操作之前。

只需要对于当前子树的每条路径,在2棵线段树上分别查询对应长度区间的答案最大值然后合并即可。

注意从颜色相同线段树上查询到的答案合并时需要减一。

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 401000
#define ac 850000
#define inf 9187201950435737472LL int n, m, rot, lim_l, lim_r, cnt, tinct, top, ss, all, id;
int Head[AC], date[ac], Next[ac], color[ac], tot;
int Size[AC];
LL power[AC], s[AC], f[AC], have[AC], ans = -inf;
bool z[AC]; struct road{
int x, y, c;
}way[ac]; inline int read()
{
int x = 0;char c = getchar();bool z_ = false;
while(c > '9' || c < '0') {if(c == '-') z_ = true; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
if(!z_) return x;
else return -x;
} inline void upmin(int &a, int b) {if(b < a) a = b;}
inline void upmax(LL &a, LL b) {if(b > a) a = b;}
inline void add(int f, int w, int S){date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, color[tot] = S;}
inline bool cmp(road a, road b){return (a.c < b.c);} struct seg_tree{ LL tree[ac]; int ls[ac], rs[ac], cnt, root; void init() {cnt = root = 1, tree[1] = tree[0] = -inf, ls[1] = rs[1] = 0;}
int make() {tree[++ cnt] = -inf, ls[cnt] = rs[cnt] = 0; return cnt;}
void update(int x) {tree[x] = max(tree[ls[x]], tree[rs[x]]);} void ins(int &x, int l, int r, int go, LL w)//只有单点修改?
{
if(!x) x = make();
if(l == r){upmax(tree[x], w); return ;}
int mid = (l + r) >> 1;
if(go <= mid) ins(ls[x], l, mid, go, w);
else ins(rs[x], mid + 1, r, go, w);
update(x);
} LL find(int x, int l, int r, int ll, int rr)
{
if(!x) return -inf;
if(l == ll && r == rr) return tree[x];
int mid = (l + r) >> 1;
if(rr <= mid) return find(ls[x], l, mid, ll, rr);
else if(ll > mid) return find(rs[x], mid + 1, r, ll, rr);
else return max(find(ls[x], l, mid, ll, mid), find(rs[x], mid + 1, r, mid + 1, rr));
} }T1, T2; void merge(){while(id) T1.ins(T1.root, 1, n, have[id], have[id - 1]), id -= 2;} void getrot(int x, int fa)
{
f[x] = 0, Size[x] = 1;
for(R i = Head[x]; i; i = Next[i])
{
int now = date[i];
if(z[now] || now == fa) continue;
getrot(now, x);
upmax(f[x], Size[now]);
Size[x] += Size[now];
}
upmax(f[x], ss - Size[x]);
if(f[x] < f[rot]) rot = x;
} void dfs(int x, int fa, int last, int num)//找到当前子树的每条线段并加入线段树
{
//T2.ins(1, 1, n, num, f[x]);
if(num >= lim_l && num <= lim_r) upmax(ans, f[x]);//不拐弯
if(num > lim_r) return ;
s[++ top] = have[++ id] = f[x], s[++ top] = have[++id] = num;
int l = max(lim_l - num, 1), r = min(n, lim_r - num);
if(l <= r)
{
upmax(ans, T2.find(1, 1, n, l, r) + f[x] - power[tinct]);
upmax(ans, T1.find(1, 1, n, l, r) + f[x]);
}
for(R i = Head[x]; i; i = Next[i])
{
int now = date[i];
if(z[now] || now == fa) continue;
f[now] = f[x] + ((color[i] == last) ? 0 : power[color[i]]);
dfs(now, x, color[i], num + 1);
}
} void cal(int x)
{
z[x] = true;
T1.init(), T2.init();
for(R i = Head[x]; i; i = Next[i])
{
int now = date[i];
if(z[now]) continue;
tinct = color[i], f[now] = power[tinct], dfs(now, x, color[i], 1);
while(top) T2.ins(T2.root, 1, n, s[top], s[top - 1]), top -= 2;//放到后面再加入防止用到同一棵子树的点
if(color[Next[i]] != color[i]) merge(), T2.init();
}
} void solve(int x)
{
//printf("%d\n", x);
cal(x);
for(R i = Head[x]; i; i = Next[i])
{
int now = date[i];
if(z[now]) continue;
rot = 0, f[0] = ss = Size[now];
getrot(now, 0);
solve(rot);
}
} void pre()
{
n = read(), m = read(), lim_l = read(), lim_r = read();
for(R i = 1; i <= m; i ++) power[i] = read();
for(R i = 1; i < n; i ++)
{
way[++ all].x = read(), way[all].y = read(), way[all].c = read();
way[all + 1] = way[all], ++all, swap(way[all].x, way[all].y);
}
sort(way + 1, way + all + 1, cmp);
for(R i = 1; i <= all; i ++) add(way[i].x, way[i].y, way[i].c);
} int main()
{
// freopen("in.in", "r", stdin);
pre();
f[rot] = ss = n;//f[x]表示x的子树中最重的那棵的重量
getrot(1, 0);
solve(rot);
printf("%lld\n", ans);
// fclose(stdin);
return 0;
}

[BJOI2017]树的难题 点分治 线段树的更多相关文章

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

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

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

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

  3. BZOJ4860 BJOI2017 树的难题 点分治、线段树合并

    传送门 只会线段树……关于单调队列的解法可以去看“重建计划”一题. 看到路径长度$\in [L,R]$考虑点分治.可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边 ...

  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 ...

随机推荐

  1. yum指令常用参数说明

    1.使用YUM查找软件包 命令:yum search 2.列出所有可安装的软件包 命令:yum list 3.列出所有可更新的软件包 命令:yum list updates 4.列出所有已安装的软件包 ...

  2. gitlab+jenkins持续集成(三)

    构建: 需要将jenkins服务器上  jenkins用户的公钥发送给  目标服务器的gs用户,使得在jenkins上能用gs免密登录目标服务器 复制密钥到目标机器上(需要登录到的机器) ssh-co ...

  3. web小结

    一.ajax 1.用于前端向服务器异步获取数据 json数组:可以直接通过数组下标获取到值 json对象:可以用“data.xx”获取到值 2.注意事项 同时请求两个ajax时,容易出现异常,第一个a ...

  4. Table 组件构建过程中遇到的问题与解决思路

    在 GearCase 开源项目构建 Table 组件的过程中.遇到了各式各样的问题,最后尝试了各种方法去解决这些问题. 遇到的部分问题 checkbox 的全选和半选问题 table 组件的排序请求方 ...

  5. Python基础_可迭代的/迭代器/生成器

    介绍 可迭代的:内部实现了__iter__方法 迭代器:内部实现了__iter__,__next__方法 生成器:yield,yield from 使用 __iter__() __next__() _ ...

  6. jenkins展示report测试报告的配置

    HTML报告展示 1. 需要HTML Publisher plugin插件 2. 在workspace下的工程(构建)中的目录中存储测试报告 在Jenkins中新建一个job,进入配置项. 首先通过p ...

  7. OO学习第一阶段总结

    前言 虽然之前接触过java,也写过一些1000行左右的程序.可以说面向对象的思想和java的一些基本语法对我来说是没有难度的,但是这学期的面向对象依然给了我一个下马威.这几次的作业每次都很让我头疼. ...

  8. 预备作业02 : 体会做中学(Learning By Doing)

    1.你有什么技能比大多人(超过班级90%以上)更好? 我认为我是一个比较爱摄影和绘画的人,虽然说说不上技术精湛,但还是能拿出手的. 2.针对这个技能的获取你有什么成功的经验? 接触摄影和绘画都是因为喜 ...

  9. JavaScript实现弹出层(以layer.open为例)

    首先,引用layer,自行下载. 添加如下两行 <script src=" ../layer/jquery.min.js"></script> <sc ...

  10. 《Spring1之第八次站立会议》

    <第八次站立会议> 昨天:我查找了关于实现视频功能的相关代码. 今天:对用C#写的视频功能进行了相关的了解. 遇到的问题:由于对C#不是很了解,所以其中的有些代码还是看不懂.