洛谷题面传送门

咦?鸽子 tzc 竟然来补题解了?incredible(

首先看到这样类似于路径统计的问题我们可以非常自然地想到点分治。每次我们找出每个连通块的重心 \(x\) 然后以 \(x\) 为根 DFS 一遍整个子树,我们假设 \(y\) 到 \(x\) 的距离为 \(dep_y\),\(x\to y\) 这一段上颜色的权值之和为 \(sum_y\),那么考虑怎样合并两条路径。显然对于两个在 \(x\) 不同子树内的点 \(y,z\),\(y\to z\) 路径上边的个数就是 \(dep_y+dep_z\),路径上权值之和就是 \(sum_y+sum_z-c_{col_y}·[col_y=col_z]\),其中 \(col_y\) 为 \(x\to y\) 路径上经过的第一条边的权值。看到这个 \([col_y=col_z]\) 貌似有点棘手,不过注意到我们贡献显然是一个子树一个子树计算的对吧,因此我们考虑将 \(x\) 所有子树按 \(x\) 到这棵子树经过的第一条边的颜色从小到大排序,然后维护两棵线段树,第一棵线段树上下标为 \(d\) 的位置上维护 \(\max\limits_{dep_y=d\land col_y\ne C}sum_y\),第二棵维护 \(\max\limits_{dep_y=d\land col_y=C}sum_y\),其中 \(C\) 为当前颜色种类,然后每次颜色改变就暴力地将第二棵线段树中所有元素插入第一棵线段树中即可,查询就在两棵树中分别查 \([r-dep_y,l-dep_y]\) 的最大值,记作 \(mx1\) 和 \(mx2\),然后用 \(mx1+sum_y,mx2-c_{col_y}+sum_y\) 更新答案即可。

时间复杂度 \(n\log^2n\),其中一个 \(\log\) 在于点分治,一个 \(\log\) 在于线段树。

最后稍微总结一下这类点分治解决树上路径计数题目的解题技巧:首先要考虑怎样合并两段路径,如果不好合并那一般使用点分治不太好解决,其次要思考如何维护两段路径的决策,比较简单的使用一个桶即可维护,比较复杂的需用 BIT/线段树/平衡树解决。对于计数问题,容斥也是一个不错的选择,即先不考虑两个端点不在同一子树这一条件,先一股脑把贡献全加上去,再扣掉在同一子树内的情况。

const int MAXN=2e5;
const int INF=0x3f3f3f3f;
const ll INFll=0x3f3f3f3f3f3f3f3fll;
int n,m,L,R,c[MAXN+5],hd[MAXN+5],to[MAXN*2+5],val[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
int mx[MAXN+5],cent=0,siz[MAXN+5];bool vis[MAXN+5];
void findcent(int x,int f,int tot){
mx[x]=0;siz[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f||vis[y]) continue;
findcent(y,x,tot);siz[x]+=siz[y];
chkmax(mx[x],siz[y]);
} chkmax(mx[x],tot-siz[x]);
if(mx[cent]>mx[x]) cent=x;
}
int dep[MAXN+5];ll sum[MAXN+5];
void getdep(int x,int f,int pre){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f||vis[y]) continue;
dep[y]=dep[x]+1;sum[y]=sum[x]+((z==pre)?0:c[z]);
getdep(y,x,z);
}
}
struct segtree{
struct node{int l,r;ll mx;} s[MAXN*4+5];
stack<int> stk;
void pushup(int k){s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);stk.push(k);}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;s[k].mx=-INFll;if(l==r) return;
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void modify(int k,int p,ll v){
if(s[k].l==s[k].r) return chkmax(s[k].mx,v),stk.push(k),void();
int mid=s[k].l+s[k].r>>1;(p<=mid)?modify(k<<1,p,v):modify(k<<1|1,p,v);
pushup(k);
}
ll query(int k,int l,int r){
if(l>r) return -INFll;
if(l<=s[k].l&&s[k].r<=r) return s[k].mx;
int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
void relax(){
while(!stk.empty()){
int k=stk.top();stk.pop();
s[k].mx=-INFll;
}
}
} s1,s2;
vector<int> pt;
ll res=-INFll;
void findpts(int x,int f){
pt.pb(x);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f||vis[y]) continue;
findpts(y,x);
}
}
void divcent(int x){
// printf("divcent %d\n",x);
vis[x]=1;dep[x]=sum[x]=0;
vector<pii> sub;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(vis[y]) continue;
dep[y]=1;sum[y]=c[z];getdep(y,x,z);
sub.pb(mp(z,y));
} sort(sub.begin(),sub.end());
vector<int> wt;s1.modify(1,0,0);
for(int i=0;i<sub.size();i++){
if(i&&sub[i].fi!=sub[i-1].fi){
s2.relax();
for(int y:wt) s1.modify(1,dep[y],sum[y]);
wt.clear();
} int y=sub[i].se;pt.clear();findpts(y,x);
for(int z:pt){
int d=dep[z];
if(d<=R){
chkmax(res,s1.query(1,max(L-d,0),R-d)+sum[z]);
chkmax(res,s2.query(1,max(L-d,0),R-d)+sum[z]-c[sub[i].fi]);
}
} for(int z:pt) wt.pb(z),s2.modify(1,dep[z],sum[z]);
// for(int z:pt) printf("%d %d %lld\n",z,dep[z],sum[z]);
} s1.relax();s2.relax();
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(vis[y]) continue;cent=0;
findcent(y,x,siz[y]);divcent(cent);
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&L,&R);s1.build(1,0,n);s2.build(1,0,n);
for(int i=1;i<=m;i++) scanf("%d",&c[i]);
for(int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),adde(u,v,w),adde(v,u,w);
mx[0]=INF;findcent(1,0,n);divcent(cent);
printf("%lld\n",res);
return 0;
}

洛谷 P3714 - [BJOI2017]树的难题(点分治)的更多相关文章

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

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

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

    题面 [BJOI2017]树的难题 题解 考虑点分治. 对于每个点,将所有边按照颜色排序. 那么只需要考虑如何合并2条链. 有2种情况. 合并路径的接口处2条路径颜色不同 合并路径的接口处2条路径颜色 ...

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

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

  4. 并不对劲的loj2179:p3714:[BJOI2017]树的难题

    题目大意 有一棵树,\(n\)(\(n\leq2*10^5\))个点,每条边\(i\)有颜色\(w_i\),共有\(m\)(\(m\leq n\))种颜色,第\(i\)种颜色的权值是\(c_i\)(\ ...

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

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

  6. luoguP3714 [BJOI2017]树的难题 点分治

    以后传数组绝对用指针... 考虑点分治 在点分的时候,把相同的颜色的在一起合并 之后,把不同颜色依次合并 我们可以用单调队列做到单次合并$O(n + m)$ 如果我们按照深度大小来合并,那么由于每次都 ...

  7. 洛谷1087 FBI树 解题报告

    洛谷1087 FBI树 本题地址:http://www.luogu.org/problem/show?pid=1087 题目描述 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全 ...

  8. 洛谷P3018 [USACO11MAR]树装饰Tree Decoration

    洛谷P3018 [USACO11MAR]树装饰Tree Decoration树形DP 因为要求最小,我们就贪心地用每个子树中的最小cost来支付就行了 #include <bits/stdc++ ...

  9. NOIP2017提高组Day2T3 列队 洛谷P3960 线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/9265380.html 题目传送门 - 洛谷P3960 题目传送门 - LOJ#2319 题目传送门 - Vij ...

随机推荐

  1. STM32中操作寄存器GPIOB_CRL &= ~( 0x0F<< (4*0))与GPIOB_CRL &=~(0x0F)之间有什么区别吗?

    没有区别,作用相同.只是这样写便于修改和沿用. 对于只用到PB0端口的程序~(0x0f << (4*0)) 和~0x0f没有区别.0x0f <<(4*N) 就是 向左 移动N个 ...

  2. Linux信号处理编程

    01. 学习目标 了解信号中的基本概念 熟练使用信号相关的函数 了解内核中的阻塞信号集和未决信号集作用 熟悉信号集操作相关函数 熟练使用信号捕捉函数signal 熟练使用信号捕捉函数sigaction ...

  3. 【UE4】 补丁Patch 与 DLC

    概述 UE4 中主要使用 Project Launcher 来进行补丁和DLC的制作 补丁与 DLC 都需要基于某个版本而制作 补丁 与 DLC 最后以 Pak 形式表现, 补丁的 pak 可以重命名 ...

  4. 【Python从入门到精通】(二)怎么运行Python呢?有哪些好的开发工具(PyCharm)

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 这是Pyhon系列文章的第二篇,本文主要介绍如何运行Python程序以及安装PyCharm开发工具. 干货满满,建议收藏,需要用到时常看看. 小伙 ...

  5. RogrePirates Scrum Meeting 博客汇总

    RogrePirates 博客目录 一.Scrum Meeting 1.Alpha阶段 第一次会议 第二次会议 第三次会议 第四次会议 第五次会议 第六次会议 第七次会议 第八次会议 第九次会议 第十 ...

  6. rabbitmq生产者消息确认

    在使用 RabbitMQ 的时候,有时候当我们生产者发送一条消息到 RabbitMQ 服务器后,我们 生产者想知道消息是否到达了 RabbitMQ 服务器上.这个时候我们应该如何处理? 针对上述问题, ...

  7. 零基础学习C语言字符串操作总结大全

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, ...

  8. 洛谷 P3195 [HNOI2008] 玩具装箱

    链接: P3195 题意: 给出 \(n\) 个物品及其权值 \(c\),连续的物品可以放进一个容器,如果将 \(i\sim j\) 的物品放进一个容器,产生的费用是 \(\left(j-i+\sum ...

  9. Hash算法:双重散列

    双重散列是线性开型寻址散列(开放寻址法)中的冲突解决技术.双重散列使用在发生冲突时将第二个散列函数应用于键的想法. 此算法使用: (hash1(key) + i * hash2(key)) % TAB ...

  10. poj 3537 Crosses and Crosses (SG)

    题意: 1 × n 个格子,每人每次选一个格子打上叉(不得重复),如果一个人画完叉后出现了连续的三个叉,则此人胜. 给n,判断先手胜还是先手败. 思路: 假设选择画叉的位置是i,则对方只能在前[1,i ...