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. TPO-17 C2 Reschedule part-time job in campus dining hall

    TPO-17 C2 Reschedule part-time job in campus dining hall 第 1 段 1.Listen to a conversation between a ...

  2. Windows下遍历某目录下的文件

    需求:要求遍历某个目录下的所有文件,文件夹 之前遇到过一些参考程序,其中有一种方法只能遍历 FAT32 格式的目录, 无法遍历NTFS的目录.

  3. NUMA 体系架构

    NUMA 体系架构 SMP 体系架构 NUMA 体系架构 NUMA 结构基本概念 Openstack flavor NUMA 策略 Nova 实现 NUMA 流程 1. SMP 体系架构 CPU 计算 ...

  4. New York Comic Con 2013 - 2013年纽约动漫展

    New York Comic Con - 2013年纽约动漫展 New York Comic Con is the largest pop culture event on the East Coas ...

  5. Scrum立会报告+燃尽图(Beta阶段第五次)

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2387 项目地址:https://coding.net/u/wuyy694 ...

  6. OOP 2.2 构造函数

    1.概念 成员函数的一种 名字与类名相同,可以有参数,没有返回值(void也不行) 作用:对对象进行初始化,如给成员函数赋初始值 如果定义时没有构造函数,则编译器生成一个默认无参数的构造函数 默认构造 ...

  7. mysql---时间类型详解

    mysql 日期类型 mysql 日期类型    ·  DATE (适用于"出生日期"等只需要年月日数据的日期字段) 日期.支持的范围为'1000-01-01'到'9999-12- ...

  8. Java 数组转字符

    public static String toString(int[] arr){ String temp = ""; for(int i = 0;i<arr.length; ...

  9. CSS中px和em属性的特点与区别

    详解px和em的特点和区别象素px是我们在定义CSS中经常用到的尺寸大小单位,而em在国外网站中经常被使用,px和em之间究竟有什么区别和特点呢?◆px像素(Pixel),相对长度单位.像素px是相对 ...

  10. sed条件不修改匹配

    sed '/^echo/!s/text/subtext/g' 如果是以echo开始行首的行就不进行替换. 参考sed substitution conditional