【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】烁烁的游戏的更多相关文章
- BZOJ3730 震波 和 BZOJ4372 烁烁的游戏
"震波"题意 F.A.Qs Home Discuss ProblemSet Status Ranklist Contest 入门OJ ModifyUser autoint Log ...
- BZOJ4372烁烁的游戏——动态点分治+线段树(点分树套线段树)
题目描述 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠.皮皮鼠会被 ...
- BZOJ4372 烁烁的游戏(动态点分治+线段树)
建出点分树,每个节点维护其作为点分树上lca对子树内点的贡献,线段树维护即可,同时另开一个线段树以减掉父亲重复的贡献. #include<iostream> #include<cst ...
- [BZOJ4372]烁烁的游戏(动态点分治+线段树)
和[BZOJ3730]震波几乎一样,每个点建两棵线段树分别代表它的管辖范围内以它为LCA的路径的贡献和它对父亲的贡献. 注意点分树上的点的距离在原树上不单调,所以不能有若距离超出限制就break之类的 ...
- BZOJ4372: 烁烁的游戏【动态点分治】
Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠. 题意: 给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠. 烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w ...
- BZOJ4372: 烁烁的游戏(动态点分治)
Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮 ...
- [BZOJ4372]烁烁的游戏
题面戳我 题意: 给一颗n个节点的树,边权均为1,初始点权均为0,m次操作: Q x:询问x的点权. M x d w:将树上与节点x距离不超过d的节点的点权均加上w. \(1≤n≤10^5\) sol ...
- [bzoj4372] 烁烁的游戏 [动态点分治+线段树+容斥原理]
题面 传送门 思路 观察一下题目,要求的是修改"距离点$u$的距离一定的点权值",那这个就不能用传统的dfs序类算法+线段树维护,因为涉及到向父亲回溯的问题 看到和树上距离相关的东 ...
- 【BZOJ4372】烁烁的游戏(动态点分治)
[BZOJ4372]烁烁的游戏(动态点分治) 题面 BZOJ 大意: 每次在一棵书上进行操作 1.将离某个点u的距离不超过d的点的权值加上w 2.询问单点权值 题解 这题和前面那一道震波几乎是一模一样 ...
- 【BZOJ4372】烁烁的游戏 动态树分治+线段树
[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距 ...
随机推荐
- C++自学第一课:函数
此贴并非教学,主要是自学笔记,所述内容只是些许个人学习心得的记录和备查积累,难以保证观点正确,也不一定能坚持完成. 如不幸到访,可能耽误您的时间,也难及时回复,贴主先此致歉.如偶有所得,相逢有缘,幸甚 ...
- c字符数组里的中文
char *p ="你abc"; strlen(p); //6 utf-8编码中
- 【RL系列】从蒙特卡罗方法步入真正的强化学习
蒙特卡罗方法给我的感觉是和Reinforcement Learning: An Introduction的第二章中Bandit问题的解法比较相似,两者皆是通过大量的实验然后估计每个状态动作的平均收益. ...
- Paper Reading - Deep Visual-Semantic Alignments for Generating Image Descriptions ( CVPR 2015 )
Link of the Paper: https://arxiv.org/abs/1412.2306 Main Points: An Alignment Model: Convolutional Ne ...
- Centos 7 zabbix 实战应用
实际需求:公司已经有了100台服务器,现在需要使用zabbix全部监控起来. 先出个方案(规划) 常规监控:cpu,内存,磁盘,网卡 问题:怎样快速添加100台机器 方法1:使用克隆的 ...
- [redis] linux下安装篇(1)
一.redis是什么redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset(有 ...
- underscore.js源码解析(四)
没看过前几篇的可以猛戳这里: underscore.js源码解析(一) underscore.js源码解析(二) underscore.js源码解析(三) underscore.js源码GitHub地 ...
- lintcode-201-线段树的构造
201-线段树的构造 线段树是一棵二叉树,他的每个节点包含了两个额外的属性start和end用于表示该节点所代表的区间.start和end都是整数,并按照如下的方式赋值: 根节点的 start 和 e ...
- nodejs 中on 和 emit
首先测试用例: var EventEmitter = require('events').EventEmitter var life = new EventEmitter(); // life.on( ...
- java沙盒
JAVA的安全模型不同于传统的安全方法,传统的安全方法中,大多数操作系统允许应用程序充分访问系统资源,在操作系统不提供安全保护的机器里,运行环境不能被信任.为了弥补这个缺陷,安全策略经常要求在应用程序 ...