Portal -->bzoj4372

Solution

  感觉自己动态点分治好像没有学好qwq今天借这题来补个档qwq

  其实所谓的动态点分治大概就是。。和点分一样的套路,但是不同的是我们要更进一步利用好每次找重心而产生的优秀性质:深度期望\(log\)

  我们考虑按照点分治那样每次找重心,但是不同的是我们要记录当前层递归的前一层的重心(记为\(pre[rt]=fa\),其中\(fa\)是上一层递归找到的重心,\(rt\)是当前层找到的重心)

  然后每个重心会有一个管辖范围(具体一点就是\(rt\)是在哪个范围内为重心),我们考虑将整个管辖范围内点的对应信息记录到这个重心上(在不同的题里面记录方式不同,比如说这题的话就是对于每一个重心开一棵线段树或者树状数组来记录)

  然后每次查询的话(比如说查询点\(x\)),我们就从\(x\)开始,每次跳到上一层递归的重心,调用重心中存储的与\(x\)有关的值,直到根,因为建点分树的方式是每层找重心,所以期望深度是\(log\)级别的

  修改的话同理(比如说修改点\(x\)),也是从\(x\)开始,每次跳到上一层递归的重心,修改重心中存储的与\(x\)有关的值,直到根,同样也是\(log\)级别的

  听起来很暴力

  但是。。点分治本来不就是一个让你的暴力跑得飞快的黑科技吗哈哈哈哈哈

  

  回到这道题

  这题中我们需要维护的显然是每个点的权值,考虑在每个重心处维护的信息是管辖范围内每个点修改了多少(类似打了个修改标记一样),然后因为修改与这个点本身的\(dep\)有关(因为钦定了每条边的边权是\(1\)嘛那就是深度咯),所以我们考虑按照管辖范围内每个点到重心的距离从小到大排序为下标,用线段树或者树状数组来维护答案

  这样有一个好处,就是修改的时候比较方便操作。我们考虑在一开始建点分树的时候,将每个重心的管辖范围内的每个点排完序之后的顺序记录下来(将这个数组记为\(id\)),同时记录排完序之后的到重心的距离(将这个数组记为\(rec\)),那么我们只需要在这个记录距离的\(rec\)数组里二分一下,找到满足题目条件的最靠后的那个点(其实就是直接upper_bound一下就好了)在记录数组中的下标\(pos\),然后将区间\([1,pos]\)中的点值全部加上\(w\)就好了

  这里需要注意的是,因为我们在\(rec\)数组中存的是到当前层重心的距离,所以每次在\(rec\)中二分的时候带进去的查询值应该要减去目标点到当前层重心的距离

  接下来还有一个小问题,就是由于当前重心\(rt\)的管辖范围也在上一层重心的管辖范围内,所以在往上跳修改的时候可能会有一部分的点被修改了两次,那么这个时候我们需要小小的容斥一下,具体一点的话就是:假设我们当前的修改操作是\(M\ x\ d\ w\),当前层的重心是\(rt\),我们要将\(pre[rt]\)中与\(rt\)中计算重复的部分减去,那么我们应该将\(rec\)值\(\in [0,d-dis(pre[rt],x)-1]\)的这些点在\(rt\)这层的修改值去掉,因为在\(pre[rt]\)这层会修改到(然而实际操作起来的时候我们可以开两棵线段树(或者树状数组),一棵记录加的标记,一棵记录容斥要减掉的标记)

  最后就是查询,查询相对来说就会没那么繁琐了,因为在修改中我们已经容斥过了,所以查询的时候直接从目标点开始一直往根跳然后每次将该层的重心中记录的标记累加进去就好了

​  不过如果写线段树的话。。空间和时间都会相对来说吃紧一点(特别对于我这种辣鸡自带常数的选手qwq)所以保险一点还是写树状数组吧反正。。这题是区间修改单点查询嘛问题不大(所以我到底是怎么做到明明写的是树状数组还写了那么长的qwq)

  然而实际上。。貌似写线段树的话如果改成单点修改区间查询这样的方式(其实就是树状数组那种套路),不需要标记下传之类的也能做到常数十分优秀并且代码简洁

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define TOP 20
#define vct vector<int>
using namespace std;
const int N=1e5+10,BIT=N*17*2;
struct xxx{
int y,nxt;
}a[N*2];
char s[10];
vct rec[N][2];
int f[N][TOP+1],dep[N],vis[N];
int h[N],sz[N],mx[N],belong[N],tmprec[N],tmpdep[N];
int df_dep[N],pre[N],df_t[N],df_sz[N];
int id[TOP+1][N][2];
int n,m,tot,rt,rt_sz,ans;
namespace Bit{/*{{{*/
int c[BIT],st[N][2],mx[N][2];
int tot=0;
void _update(int which,int x,int delta,int op){
for (;x<=mx[which][op];x+=x&-x)
c[st[which][op]+x]+=delta;
}
void update(int which,int op,int l,int r,int delta){
_update(which,l,delta,op);
if (r<mx[which][op]) _update(which,r+1,-delta,op);
}
int query(int which,int op,int x){
int ret=0;
for (;x;x-=x&-x) ret+=c[st[which][op]+x];
return ret;
}
}/*}}}*/
//orignal tree{{{
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void predfs(int fa,int x,int d){
int u;
dep[x]=d; f[x][0]=fa;
for (int i=1;i<=TOP;++i) f[x][i]=f[f[x][i-1]][i-1];
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
predfs(x,u,d+1);
}
}
int get_lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
for (int i=TOP;i>=0;--i)
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
for (int i=TOP;i>=0;--i)
if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int get_dis(int x,int y){
int lca=get_lca(x,y);
return dep[x]+dep[y]-2*dep[lca];
}
/*}}}*/
//get_rt{{{
void get_sz(int fa,int x){
int u;
sz[x]=1; mx[x]=0;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
get_sz(x,u);
sz[x]+=sz[u];
if (sz[u]>mx[x]) mx[x]=sz[u];
}
}
void get_rt(int Rt,int fa,int x){
int u;
mx[x]=max(mx[x],sz[Rt]-sz[x]);
if (mx[x]<=rt_sz) rt=x,rt_sz=mx[x];
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
get_rt(Rt,x,u);
}
}/*}}}*/
//df-tree{{{
void dfs(int fa,int x,int d){
int u;
tmprec[++tmprec[0]]=x; tmpdep[x]=d;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||vis[u]) continue;
dfs(x,u,d+1);
}
}
bool cmp(int x,int y){return tmpdep[x]<tmpdep[y];}
void get_lis(int x,int rt,int which_t,int op){
tmprec[0]=0;
Bit::st[rt][op]=Bit::tot;
if (op==0)
dfs(0,rt,0),Bit::mx[rt][op]=tmprec[0];
else
dfs(0,x,0),Bit::mx[rt][op]=Bit::mx[rt][0];
Bit::tot+=tmprec[0]; sort(tmprec+1,tmprec+1+tmprec[0],cmp);
rec[rt][op].resize(tmprec[0]);
for (int i=1;i<=tmprec[0];++i){
id[which_t][tmprec[i]][op]=i;
rec[rt][op][i-1]=tmpdep[tmprec[i]];
}
}
void solve(int fa,int x,int Sz){
int u,Rt;
rt=0,rt_sz=n;
get_sz(0,x);
get_rt(x,0,x);
Rt=rt; df_t[Rt]=df_t[fa]+1; df_sz[Rt]=Sz;
pre[Rt]=fa;
get_lis(x,Rt,df_t[Rt],0);
get_lis(x,Rt,df_t[Rt],1);
vis[Rt]=true;
for (int i=h[Rt];i!=-1;i=a[i].nxt){
u=a[i].y;
if (vis[u]) continue;
solve(Rt,u,sz[u]>sz[Rt]?Sz-sz[Rt]:sz[u]);
}
}
void modify(int x,int aim,int d,int delta){
int dis=d-get_dis(x,aim),pos;
pos=upper_bound(rec[x][0].begin(),rec[x][0].end(),dis)-rec[x][0].begin();
Bit::update(x,0,1,pos,delta);
if (pre[x]){
dis=d-get_dis(pre[x],aim)-1;
if (dis>=0){
pos=upper_bound(rec[x][1].begin(),rec[x][1].end(),dis)-rec[x][1].begin();
Bit::update(x,1,1,pos,-delta);
}
modify(pre[x],aim,d,delta);
}
}
int query(int x,int aim){
int ret=Bit::query(x,0,id[df_t[x]][aim][0]);
if (pre[x]){
ret+=Bit::query(x,1,id[df_t[x]][aim][1]);
ret+=query(pre[x],aim);
}
return ret;
}
/*}}}*/ int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,d,w;
scanf("%d%d",&n,&m);
memset(h,-1,sizeof(h));
tot=0;
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
predfs(0,1,1);
solve(0,1,n);
for (int i=1;i<=m;++i){
scanf("%s",s);
if (s[0]=='Q'){
scanf("%d",&x);
ans=query(x,x);
printf("%d\n",ans);
}
else{
scanf("%d%d%d",&x,&d,&w);
modify(x,x,d,w);
}
}
}

【bzoj4372】烁烁的游戏的更多相关文章

  1. BZOJ3730 震波 和 BZOJ4372 烁烁的游戏

    "震波"题意 F.A.Qs Home Discuss ProblemSet Status Ranklist Contest 入门OJ ModifyUser  autoint Log ...

  2. BZOJ4372烁烁的游戏——动态点分治+线段树(点分树套线段树)

    题目描述 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠.皮皮鼠会被 ...

  3. BZOJ4372 烁烁的游戏(动态点分治+线段树)

    建出点分树,每个节点维护其作为点分树上lca对子树内点的贡献,线段树维护即可,同时另开一个线段树以减掉父亲重复的贡献. #include<iostream> #include<cst ...

  4. [BZOJ4372]烁烁的游戏(动态点分治+线段树)

    和[BZOJ3730]震波几乎一样,每个点建两棵线段树分别代表它的管辖范围内以它为LCA的路径的贡献和它对父亲的贡献. 注意点分树上的点的距离在原树上不单调,所以不能有若距离超出限制就break之类的 ...

  5. BZOJ4372: 烁烁的游戏【动态点分治】

    Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠. 题意: 给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠. 烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w ...

  6. BZOJ4372: 烁烁的游戏(动态点分治)

    Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮 ...

  7. [BZOJ4372]烁烁的游戏

    题面戳我 题意: 给一颗n个节点的树,边权均为1,初始点权均为0,m次操作: Q x:询问x的点权. M x d w:将树上与节点x距离不超过d的节点的点权均加上w. \(1≤n≤10^5\) sol ...

  8. [bzoj4372] 烁烁的游戏 [动态点分治+线段树+容斥原理]

    题面 传送门 思路 观察一下题目,要求的是修改"距离点$u$的距离一定的点权值",那这个就不能用传统的dfs序类算法+线段树维护,因为涉及到向父亲回溯的问题 看到和树上距离相关的东 ...

  9. 【BZOJ4372】烁烁的游戏(动态点分治)

    [BZOJ4372]烁烁的游戏(动态点分治) 题面 BZOJ 大意: 每次在一棵书上进行操作 1.将离某个点u的距离不超过d的点的权值加上w 2.询问单点权值 题解 这题和前面那一道震波几乎是一模一样 ...

  10. 【BZOJ4372】烁烁的游戏 动态树分治+线段树

    [BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距 ...

随机推荐

  1. 学习笔记之ubuntu修改固定IP脚本

    一.shell脚本编程 二.正则表达式 三.linux修改IP的方法 #!/bin/bash cd /etc/network/ stty erase '^?' write_interfaces() { ...

  2. jquery中国地图插件

    插件下载地址: http://www.17sucai.com/preview/1266961/2018-09-18/map/js/jsMap-1.1.0.min.js jsMap 项目介绍 这是一个功 ...

  3. CsvHelper文档-6类型转换

    CsvHelper文档-6类型转换 CsvHelper使用类型转换器来转换string到对象,或者对象到string: ITypeConverter 类型转换器的结构,必须实现: public int ...

  4. Windows单机配置Kafka环境

    首先确保机器已经安装好Zookeeper,Zookeeper安装参考 Windows单机配置Zookeeper环境 然后确保Zookeeper是正常启动状态 下载Kafka http://kafka. ...

  5. scikit-learn使用PCA降维小结

    本文在主成分分析(PCA)原理总结和用scikit-learn学习主成分分析(PCA)的内容基础上做了一些笔记和补充,强调了我认为重要的部分,其中一些细节不再赘述. Jupiter notebook版 ...

  6. PowerDesigne 建立概念数据模型

    本文主要介绍PowerDesigner概念数据模型以及实体.属性创建. 一.新建概念数据模型1)选择File-->New,弹出如图所示对话框,选择CDM模型(即概念数据模型)建立模型. 2)完成 ...

  7. 《Linux内核与分析》第六周

    20135130王川东 1.操作系统的三大管理功能包括:进程管理,内存管理,文件系统. 2. Linux内核通过唯一的进程标识PID来区别每个进程.为了管理进程,内核必须对每个进程进行清晰的描述,进程 ...

  8. “我爱淘”第二冲刺阶段Scrum站立会议6

    完成任务: 完成学院分类的点击查看书籍功能,可以点击书的条目查看书的详细信息. 计划任务: 将书的详细信息进行完善,并且可以点击收藏以及已预订等功能. 遇到问题: 分类功能,根据不同学院,自动将数据库 ...

  9. POJ 2484(对称博弈)

    题目链接:http://poj.org/problem?id=2484 这道题目大意是这样的,有n个硬币围成一圈,两个人轮流开始取硬币(假设他们编号从1到n),可以选择取一枚或者取相邻的两枚(相邻是指 ...

  10. FPGA论文

    基于 NetFPGA 的 VCP 网络的设计与实现 --可变结构拥塞控制协议(VCP),适应于高带宽时延乘积网络的显式拥塞控制协议 无源光网络(PON) 1.区块链技术发展,物联网设备激增,服务器压力 ...