洛谷 P3714 - [BJOI2017]树的难题(点分治)
咦?鸽子 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]树的难题(点分治)的更多相关文章
- P3714 [BJOI2017]树的难题 点分治+线段树合并
题目描述 题目传送门 分析 路径问题考虑点分治 对于一个分治中心,我们可以很容易地得到从它开始的一条路径的价值和长度 问题就是如何将不同的路径合并 很显然,对于同一个子树中的所有路径,它们起始的颜色是 ...
- [BJOI2017]树的难题 点分治 线段树
题面 [BJOI2017]树的难题 题解 考虑点分治. 对于每个点,将所有边按照颜色排序. 那么只需要考虑如何合并2条链. 有2种情况. 合并路径的接口处2条路径颜色不同 合并路径的接口处2条路径颜色 ...
- [BJOI2017]树的难题 点分治,线段树合并
[BJOI2017]树的难题 LG传送门 点分治+线段树合并. 我不会写单调队列,所以就写了好写的线段树. 考虑对于每一个分治中心,把出边按颜色排序,这样就能把颜色相同的子树放在一起处理.用一棵动态开 ...
- 并不对劲的loj2179:p3714:[BJOI2017]树的难题
题目大意 有一棵树,\(n\)(\(n\leq2*10^5\))个点,每条边\(i\)有颜色\(w_i\),共有\(m\)(\(m\leq n\))种颜色,第\(i\)种颜色的权值是\(c_i\)(\ ...
- BZOJ4860 BJOI2017 树的难题 点分治、线段树合并
传送门 只会线段树……关于单调队列的解法可以去看“重建计划”一题. 看到路径长度$\in [L,R]$考虑点分治.可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边 ...
- luoguP3714 [BJOI2017]树的难题 点分治
以后传数组绝对用指针... 考虑点分治 在点分的时候,把相同的颜色的在一起合并 之后,把不同颜色依次合并 我们可以用单调队列做到单次合并$O(n + m)$ 如果我们按照深度大小来合并,那么由于每次都 ...
- 洛谷1087 FBI树 解题报告
洛谷1087 FBI树 本题地址:http://www.luogu.org/problem/show?pid=1087 题目描述 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全 ...
- 洛谷P3018 [USACO11MAR]树装饰Tree Decoration
洛谷P3018 [USACO11MAR]树装饰Tree Decoration树形DP 因为要求最小,我们就贪心地用每个子树中的最小cost来支付就行了 #include <bits/stdc++ ...
- NOIP2017提高组Day2T3 列队 洛谷P3960 线段树
原文链接https://www.cnblogs.com/zhouzhendong/p/9265380.html 题目传送门 - 洛谷P3960 题目传送门 - LOJ#2319 题目传送门 - Vij ...
随机推荐
- 康托展开+逆展开(Cantor expension)详解+优化
康托展开 引入 康托展开(Cantor expansion)用于将排列转换为字典序的索引(逆展开则相反) 百度百科 维基百科 方法 假设我们要求排列 5 2 4 1 3 的字典序索引 逐位处理: 第一 ...
- UltraSoft - Beta - Postmortem事后分析
UltraSoft - Beta - PostMORTEM 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 解决的问题和定义都在[软软软]功能规格说明书 ...
- Scrum Meeting 0609
零.说明 日期:2021-6-9 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 困难 qsy PM&前端 完成前端功能 ...
- UltraSoft - Alpha - Postmortem 事后分析
Alpha阶段 Postmortem会议 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 主要是解决DDL提醒功能的问题,定义的比较清楚,对典型用户和典 ...
- 主仆见证了 Hobo 的离别 题解
前言: 题面挺神仙.反正我考试的时候看了40分钟也没看懂. 后来改题感觉自己写的挺假,没想到加个\(k==1\)的特判竟然就A了?无语力. 解析: 看懂题以后就好说了.首先这显然是一个树形结构.我们考 ...
- Linux多线程实例解析
Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...
- matplotlib.legend()函数用法
用的较多,作为记录 legend语法参数如下: matplotlib.pyplot.legend(*args, **kwargs) 几个暂时主要用的参数: (1)设置图例位置 使用loc参数 plt. ...
- 攻防世界 web3.backup
如果网站存在备份文件,常见的备份文件后缀名有:.git ..svn..swp..~..bak..bash_history..bkf尝试在URL后面,依次输入常见的文件备份扩展名. ip/index.p ...
- spring cloud Alibaba --sentinel组件的使用
sentinel组件 对于sentinel的前置知识这里就不多说了: 直接上代码: Release v1.8.1 · alibaba/Sentinel · GitHub 下载地址 springclo ...
- Delphi的手机程序隐藏顶部信号栏
把TForm的BorderStyle设置为None 记之!