【XSY2307】树的难题
Description
Solution
看到这种路径统计问题,一般就想到要用点分治去做。
对于每个重心\(u\),统计经过\(u\)的合法的路径之中的最大值。
第一类路径是从\(u\)出发的,直接逐个子树深搜统计就可以了。第二类路径是由两棵不同子树中的两条第一类路径拼接而成的。
如果仅仅是统计长度在\([l,r]\)之间的路径有多少条,经典的统计+容斥做法就可以解决。然而现在的问题比较复杂,一来不好容斥,二来两两路径配对需要有判定条件:两条路径的接口边颜色是否相同。
我们可以采用一种不需要容斥的做法:逐一枚举子树,并逐一考虑由子树内的每个点出发去其他子树的路径。枚举到当前这棵子树内的某个节点\(x\)时,我们用\(sum[d]\)记录在之前的子树中,相对于\(u\)深度为\(d\)的点的某些信息。那么对于一个点\(x\),它可以接上的路径的信息应该有\(sum[i],\;i\in[l-dep_x,r-dep_x]\)。
所以\(sum[d]\)记录的是什么呢?按照题目意思,我们应该记录所有从深度为\(d\)的点出发到\(u\)路径中的路径最大值。可是注意这个最大值不一定相对所有询问点来说都是最大值,有可能此最大值对应的路径\(\alpha\)与当前询问点路径接口边相同,权值重复,需要减去一次,说不定就不是最大的了。所以,我们额外要记录一个接口颜色异于最大值路径\(\alpha\)的另一条权值最大路径\(\beta\)。
这样一来,对于当前枚举点对应的路径\(\gamma\),想要和先前子树中一条长度为\(d\)的路径结合时,有\(sum[d]=(\alpha,\beta)\)(分别为两条路径的权值)。先看\(\alpha\)的接口边颜色是否和\(\gamma\)相同,如果是,取\(\max\{\alpha-c,\beta\}\)作为最大路径的权值和\(\gamma\)拼接(\(c\)表示\(\alpha\)和\(\gamma\)的接口边颜色的权值);否则,取\(\alpha\)拼接。
有意思的是,因为我们是一个一个子树进行处理,所以当前枚举的所有点的接口边颜色都是一样的,也就意味着不同点尝试配对同一个\(sum\)时的选择都是完全一样的。因此每一个\(sum\)的贡献是定值。我们相当于有一个数列,每次求\([l-dep_x,r-dep_x]\)中的最大值。如果我们把点按照深度来排序,那么这个范围就是一个滑动窗口,可以使用单调队列进行\(\mathcal O(n)\)解决。
Code
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int N=200005,INF=2e9+5;
int n,m,l,r,cv[N],all;
int best,bestval,size[N];
int udep[N],cc[N];
int lis[N],lcnt,info[N][3];
deque<int> q;
int qv[N];
bool cut[N],vis[N];
int ans;
struct Data{
int v1,c1,v2,c2;
Data(){v1=v2=-INF; c1=c2=-1;}
inline void insert(int v,int c){
if(c!=c1){
if(v>v1)
v2=v1,c2=c1,v1=v,c1=c;
else if(v>v2)
v2=v,c2=c;
}
else if(v>v1) v1=v;
}
int get(int c){
return c==c1?max(v1-cv[c],v2):v1;
}
}s[N];
int h[N],tot;
struct Edge{int v,c,next;}e[N*2];
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void addEdge(int u,int v,int c){
e[++tot]=(Edge){v,c,h[u]}; h[u]=tot;
e[++tot]=(Edge){u,c,h[v]}; h[v]=tot;
}
void getRt(int u,int fa,int sz){
int maxsub=-1;
size[u]=1;
for(int i=h[u],v;i;i=e[i].next)
if((v=e[i].v)!=fa&&!cut[v]){
getRt(v,u,sz);
size[u]+=size[v];
maxsub=max(maxsub,size[v]);
}
maxsub=max(maxsub,sz-size[u]);
if(maxsub<bestval)
bestval=maxsub,best=u;
}
bool cmp(const int &x,const int &y){return udep[x]<udep[y];}
void dfs(int u,int fa,int dep,int &nowx){
nowx=max(nowx,dep);
for(int i=h[u],v;i;i=e[i].next)
if((v=e[i].v)!=fa&&!cut[v])
dfs(v,u,dep+1,nowx);
}
void insert(int u,int fa,int dep,int topc,int fac,int val){
vis[u]=false;
if(l<=dep&&dep<=r) ans=max(ans,val);
s[dep].insert(val,topc);
for(int i=h[u],v;i;i=e[i].next)
if((v=e[i].v)!=fa&&!cut[v])
insert(v,u,dep+1,topc,e[i].c,fac==e[i].c?val:val+cv[e[i].c]);
}
void collect(int x,int topc){
int head=1,tail=1;
lis[1]=x;
info[x][0]=1; info[x][1]=cv[topc]; info[x][2]=topc;
vis[x]=true;
while(head<=tail){
int u=lis[head++];
for(int i=h[u],v;i;i=e[i].next)
if(!cut[v=e[i].v]&&!vis[v]){
lis[++tail]=v;
vis[v]=true;
info[v][0]=info[u][0]+1;
info[v][1]=info[u][1]+(e[i].c==info[u][2]?0:cv[e[i].c]);
info[v][2]=e[i].c;
}
}
lcnt=tail;
}
void solve(int x,int sz){
best=-1; bestval=INF;
getRt(x,0,sz);
int u=best;
cut[u]=true;
static int son[N],cnt;
cnt=0;
int curr=-1;
for(int i=h[u],v;i;i=e[i].next)
if(!cut[v=e[i].v]){
son[++cnt]=v;
cc[v]=e[i].c;
dfs(v,0,1,udep[v]);
curr=max(curr,udep[son[cnt]]);
}
curr=min(r,curr);
for(int i=1;i<=curr;i++) s[i]=Data();
vis[u]=true;
for(int i=1,v;i<=cnt;i++){
v=son[i];
collect(v,cc[v]);
while(!q.empty()) q.pop_back();
int nowl,nowr,qr=0;
bool first=true;
for(int j=lcnt;j>=1;j--)
if(info[lis[j]][0]<r){
int x=lis[j];
nowl=max(1,l-info[x][0]); nowr=r-info[x][0];
if(first){
first=false;
qr=nowl-1;
while(qr<nowr&&qr<curr&&s[qr+1].v1>-INF){
int newval=s[++qr].get(cc[v]);
if(newval>-INF){
while(!q.empty()&&qv[q.back()]<=newval) q.pop_back();
q.push_back(qr);
qv[qr]=newval;
}
}
}
while(!q.empty()&&q.front()<nowl) q.pop_front();
while(qr<nowr&&qr<curr&&s[qr+1].v1>-INF){
int newval=s[++qr].get(cc[v]);
if(newval>-INF){
while(!q.empty()&&qv[q.back()]<=newval) q.pop_back();
q.push_back(qr);
qv[qr]=newval;
}
}
if(!q.empty())
ans=max(ans,info[x][1]+qv[q.front()]);
}
insert(v,0,1,cc[v],cc[v],cv[cc[v]]);
}
for(int i=h[u],v;i;i=e[i].next)
if(!cut[v=e[i].v])
solve(v,size[v]>size[u]?(sz-size[u]):size[v]);
}
int main(){
scanf("%d%d%d%d",&n,&m,&l,&r);
for(int i=1;i<=m;i++) scanf("%d",cv+i);
for(int i=1,u,v,c;i<n;i++){
scanf("%d%d%d",&u,&v,&c);
addEdge(u,v,c);
}
ans=-INF;
solve(1,n);
printf("%d\n",ans);
return 0;
}
【XSY2307】树的难题的更多相关文章
- [BJOI2017]树的难题 点分治 线段树
题面 [BJOI2017]树的难题 题解 考虑点分治. 对于每个点,将所有边按照颜色排序. 那么只需要考虑如何合并2条链. 有2种情况. 合并路径的接口处2条路径颜色不同 合并路径的接口处2条路径颜色 ...
- [BJOI2017]树的难题 点分治,线段树合并
[BJOI2017]树的难题 LG传送门 点分治+线段树合并. 我不会写单调队列,所以就写了好写的线段树. 考虑对于每一个分治中心,把出边按颜色排序,这样就能把颜色相同的子树放在一起处理.用一棵动态开 ...
- BZOJ3257 : 树的难题
设$f[x][i][j]$表示以$x$为根的子树,与$x$连通部分有$i$个黑点,$j$个白点,不联通部分都是均衡的最小代价.若$i>1$,则视作$1$:若$j>2$,则视作$2$. 然后 ...
- BZOJ4860 BJOI2017 树的难题 点分治、线段树合并
传送门 只会线段树……关于单调队列的解法可以去看“重建计划”一题. 看到路径长度$\in [L,R]$考虑点分治.可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边 ...
- [bzoj4860] [BeiJing2017]树的难题
Description 给你一棵 n 个点的无根树.树上的每条边具有颜色. 一共有 m 种颜色,编号为 1 到 m.第 i 种颜色的权值为 ci.对于一条树上的简单路径,路径上经过的所有边按顺序组成一 ...
- bzoj 4860 [BeiJing2017]树的难题
题面 https://www.lydsy.com/JudgeOnline/problem.php?id=4860 题解 点分治 设当前重心为v 假设已经把所有边按照出发点第一关键字, 颜色第二关键字排 ...
- 并不对劲的loj2179:p3714:[BJOI2017]树的难题
题目大意 有一棵树,\(n\)(\(n\leq2*10^5\))个点,每条边\(i\)有颜色\(w_i\),共有\(m\)(\(m\leq n\))种颜色,第\(i\)种颜色的权值是\(c_i\)(\ ...
- [JZOJ3347] 【NOI2013模拟】树的难题
题目 题目大意 给你一棵树,每个节点有三种黑.白.灰三种颜色. 你要割掉一些边(每条边被割需要付出一定的代价),使得森林的每棵树满足: 没有黑点或至多一个白点. 思考历程 这题一看就知道是一个树形DP ...
- G 树的难题
时间限制 : 10000 MS 空间限制 : 165536 KB 评测说明 : 1s,128m 问题描述 给出一个无根树.树有N个点,边有权值.每个点都有颜色,是黑色.白色.灰色这三种颜色之一,称 ...
随机推荐
- OpenGL学习笔记(3) 纹理
关于纹理 一般游戏里的物体不一定都是纯色的物体,物体上面会有一些图片贴在上面,比如墙壁,箱子,地板,可以看到砖头.木板和大理石组成的图片,要把图片贴到计算机里的几何图形的话,就要把图片的颜色采样贴到几 ...
- 【CodeForces-1041C】Coffee Break(二分解决关于set,pair,upper_bound用法)
//题意:一个的工作时间是m分钟. // 在特定的时间和咖啡 n a1,a2....an,, ai代表的是每个咖啡要在一天中对应的时间点喝掉 // 每一次喝咖啡的时间为1分钟 // 必须在一天中的ai ...
- zookeeper简易配置及hadoop高可用安装
zookeeper介绍 是一个分布式服务的协调服务,集群半数以上可用(一般配置为奇数台), 快速选举机制:当集群中leader挂掉,所有小弟会投票选举出新的leader. ...
- Python机器学习库SKLearn:数据集转换之管道和特征
转载自:https://blog.csdn.net/cheng9981/article/details/61918129 4.1 管道和特征:组合估计量 4.1.1 管道:链接估计 管道可以用于将多个 ...
- kubernetes集群部署mysql 8.0
参考:https://blog.csdn.net/sealir/article/details/81177747?utm_source=blogxgwz1 集群内安装mysql并添加相应存储(PVC) ...
- 【Ansible】ansible 任务失败控制
任务失败控制 Ansible 通常默认会确保检测模块和命令的返回码并且会快速失败 – 专注于一个错误除非你另作打算. 有时一条命令会返回 0 但那不是报错.有时命令不会总是报告它 ‘改变’ 了远程系统 ...
- 基于C#的机器学习--惩罚与奖励-强化学习
强化学习概况 正如在前面所提到的,强化学习是指一种计算机以“试错”的方式进行学习,通过与环境进行交互获得的奖赏指导行为,目标是使程序获得最大的奖赏,强化学习不同于连督学习,区别主要表现在强化信号上,强 ...
- 【神经网络】自编码聚类算法--DEC (Deep Embedded Clustering)
1.算法描述 最近在做AutoEncoder的一些探索,看到2016年的一篇论文,虽然不是最新的,但是思路和方法值得学习.论文原文链接 http://proceedings.mlr.press/v48 ...
- Redux和React-Redux的实现(二):Provider组件和connect的实现
接着上一篇讲,上一篇我们实现了自己的Redux和介绍了React的context以及Provider的原理. 1. Provider组件的实现 Provider组件主要有以下下两个作用 在整个应用上包 ...
- First Blood
自我介绍 大家好!我的名字是戴俊涵,代号211606359,喜欢看电影和古风音乐,也是一个资深漫迷(让世界感受痛楚吧),喜欢的美食是牛排. 回想初衷 (1)回想一下你初入大学时对本专业的畅想 当初你是 ...