震波——动态点分治+线段树

题目

【题目描述】

在一片土地上有 $N$ 个城市,通过 $N-1$ 条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为 $1$,其中第 $i$ 个城市的价值为 $value[i]$。
不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动。

接下来你需要在线处理 $M$ 次操作:
- $0~x~k$ 表示发生了一次地震,震中城市为 $x$ ,影响范围为 $k$ ,所有与 $x$ 距离不超过 $k$ 的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。
- $1~x~y$ 表示第$x$个城市的价值变成了 $y$ 。

为了体现程序的在线性,操作中的 $x$ 、$y$ 、$k$ 都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为 $0$ 。

【输入格式】

第一行包含两个正整数 $N$ 和 $M$ 。

第二行包含 $N$ 个正整数,第 $i$ 个数表示 $value[i]$ 。

接下来 $N-1$ 行,每行包含两个正整数$u,v$,表示 $u$ 和 $v$ 之间有一条无向边。

接下来 $M$ 行,每行包含三个数,表示 $M$ 次操作。

【输出格式】
包含若干行,对于每个询问输出一行一个正整数表示答案。
【样例输入】

8 1
1 10 100 1000 10000 100000 1000000 10000000
1 2
1 3
2 4
2 5
3 6
3 7
3 8
0 3 1

【样例输出】
11100101
【数据范围与提示】

$1 \le N,M \le 100000$

$1 \le u,v,x \le N $

$1 \le value[i],y \le 10000 $

$ 0 \le k \le N-1 $

题解

要求树上和一个点距离不超过 $k$ 的所有点,很容易想到动态点分治

开 $ n $ 棵权值线段树,记录当前点分中心距离为 $ v $ 的点的个数

考虑在点分树上查询,会发现在 $ x $ 统计过答案的点有可能在 $ fa[x] $ 上也被统计

考虑容斥,再开 $ n $ 棵线段树,记录从当前点分中心 $ x $ 经过 $ fa[x] $ 距离为 $ v $ 的权值和,查询时扣掉即可

时间效率:$ O(nlog^2 n) $

代码

  1 #include<bits/stdc++.h>
  2 #define LL long long
  3 #define il inline
  4 #define re register
  5 #define _(d) while(d(isdigit(ch=getchar())))
  6 using namespace std;
  7 il int R(){
  8     int x;bool f=1;char ch;_(!)if(ch==‘-‘)f=0;x=ch^48;
  9     _()x=(x<<3)+(x<<1)+(ch^48);return f?x:-x;}
 10 const int N=2e5+10;
 11 int n,m,ans,f[N],top[N],dep[N],sz[N],son[N],head[N],cnt,Mx,Rt,Sz,fa[N],siz[N],val[N],rot[N<<1],tot;
 12 bool vis[N];
 13 struct seg{int ls,rs,v;}tr[N<<6];
 14 struct edge{int to,nex;}e[N<<1];
 15 il void add(int s,int t){e[++cnt]=(edge){t,head[s]},head[s]=cnt;}
 16 il void dfs(int x,int far){
 17     f[x]=far,sz[x]=1,dep[x]=dep[far]+1;
 18     for(re int v,k=head[x];k;k=e[k].nex)
 19         if((v=e[k].to)!=far){
 20             dfs(v,x),sz[x]+=sz[v];
 21             if(sz[v]>sz[son[x]])son[x]=v;
 22         }
 23     return;
 24 }
 25 il void DFS(int x,int far){
 26     top[x]=far;
 27     if(son[x])DFS(son[x],far);
 28     for(re int k=head[x],v;k;k=e[k].nex)
 29         if((v=e[k].to)!=f[x]&&v!=son[x])
 30             DFS(v,v);
 31 }
 32 il int lca(int x,int y){
 33     while(top[x]^top[y]){
 34         if(dep[top[x]]<dep[top[y]])swap(x,y);
 35         x=f[top[x]];
 36     }
 37     return dep[x]<dep[y]?x:y;
 38 }
 39 il int get(int u,int v){return dep[u]+dep[v]-2*dep[lca(u,v)];}
 40 il void getrot(int u,int far){
 41     int mx=0;siz[u]=1;
 42     for(re int k=head[u],v;k;k=e[k].nex)
 43         if((v=e[k].to)!=far&&!vis[v])
 44             getrot(v,u),siz[u]+=siz[v],mx=max(mx,siz[v]);
 45     mx=max(mx,Sz-siz[u]);
 46     if(mx<Mx)Mx=mx,Rt=u;
 47     return;
 48 }
 49 il int getsize(int u,int far){
 50     siz[u]=1;
 51     for(re int k=head[u],v;k;k=e[k].nex)
 52         if((v=e[k].to)!=far&&!vis[v])
 53             siz[u]+=getsize(v,u);
 54     return siz[u];
 55 }
 56 void slove(int u,int far){
 57     Mx=1e9,getrot(u,0),vis[Rt]=1;
 58     fa[Rt]=far,far=Rt;
 59     for(re int k=head[Rt],v;k;k=e[k].nex)
 60         if(!vis[v=e[k].to])Sz=getsize(v,0),slove(v,far);
 61 }
 62 #define Ls tr[rt].ls
 63 #define Rs tr[rt].rs
 64 il void change(int &rt,int l,int r,int k,int x){
 65     if(!rt)rt=++tot;
 66     tr[rt].v+=x;
 67     if(l==r)return;
 68     int mid=(l+r)>>1;
 69     if(k<=mid)change(Ls,l,mid,k,x);
 70     else change(Rs,mid+1,r,k,x);
 71     return;
 72 }
 73 il int query(int rt,int l,int r,int qr){
 74     if(!rt)return 0;
 75     if(qr>=r)return tr[rt].v;
 76     int mid=(l+r)>>1,res=0;
 77     if(qr>mid)res=tr[Ls].v+query(Rs,mid+1,r,qr);
 78     else res=query(Ls,l,mid,qr);
 79     return res;
 80 }
 81 il void update(int x,int v){
 82     change(rot[x],0,n,0,v);
 83     for(re int i=x;fa[i];i=fa[i]){
 84         int dis=get(x,fa[i]);
 85         change(rot[fa[i]],0,n,dis,v);
 86         change(rot[i+n],0,n,dis,v);
 87     }
 88 }
 89 il int ask(int x,int y){
 90     int res=query(rot[x],0,n,y);
 91     for(re int i=x;fa[i];i=fa[i]){
 92         int dis=get(x,fa[i]);
 93         if(dis>y)continue;
 94         res+=query(rot[fa[i]],0,n,y-dis)-query(rot[i+n],0,n,y-dis);
 95     }
 96     return res;
 97 }
 98 int main(){
 99     n=R(),m=R();
100     for(re int i=1;i<=n;i++)val[i]=R();
101     for(re int i=1,u,v;i<n;i++)
102         u=R(),v=R(),add(u,v),add(v,u);
103     dfs(1,0),DFS(1,1),Sz=n,slove(1,0);
104     for(re int i=1;i<=n;i++)update(i,val[i]);
105     for(re int i=1;i<=m;i++){
106         int op=R(),x=R()^ans,y=R()^ans;
107         if(op)update(x,y-val[x]),val[x]=y;
108         else printf("%d\n",ans=ask(x,y));
109     }
110     return 0;
111 }

原文地址:https://www.cnblogs.com/chmwt/p/10661287.html

时间: 2024-10-09 05:58:27

震波——动态点分治+线段树的相关文章

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

[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠.皮皮鼠会被烁烁吸引,所以会一直待在节点上不动.烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠.大意:给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. In

【BZOJ-4636】蒟蒻的数列 动态开点线段树 ||(离散化) + 标记永久化

4636: 蒟蒻的数列 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 247  Solved: 113[Submit][Status][Discuss] Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k小的数改为k,他想知 道N次操作后数列中所有元素的和.他还要玩其他游戏,所以这个问题留给你解决. Input 第一

Codeforces 803G Periodic RMQ Problem ST表+动态开节点线段树

思路: (我也不知道这是不是正解) ST表预处理出来原数列的两点之间的min 再搞一个动态开节点线段树 节点记录ans 和标记 lazy=-1 当前节点的ans可用  lazy=0 没被覆盖过 else 区间覆盖 push_up的时候要注意好多细节,, 数组尽量往大开 //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const in

【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp

题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a variant of the game for themselves to play. Being played by clumsy animals weighing nearly a ton, Cow Hopscotch almost always ends in disaster, but this has

【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点. 输入 第一行有两个整数N,Q(1 ≤N≤ 100,000:1 ≤Q≤ 200,000),分别表示节点个数和操作个数. 下面一行N个整数,表示初始时每个节点的初始值. 接下来N-1行,每行两个整数x,y,表示x节点与y节点之间有边直接相连(描述一颗树).

UVALive 7148 LRIP【树分治+线段树】

题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值.如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询.同时每做完一颗子树的时候,用MN,MX对线段树进行更新.

CF1045G AI robots(动态开点线段树)

题意 火星上有$N$个机器人排成一行,第$i$个机器人的位置为$x_{i}$,视野为$r_{i}$,智商为$q_{i}$.我们认为第$i$个机器人可以看到的位置是$[x_{i}-r_{i},x_{i}+r_{i}]$.如果一对机器人相互可以看到,且它们的智商$q_{i}$的差距不大于$K$,那么它们会开始聊天. 为了防止它们吵起来,请计算有多少对机器人可能会聊天. 题解 先膜一下大佬->这里 我们先按视野降序排序,这样一个一个考虑,如果后面的能看到前面,那前面的也肯定能看到后面 这样,就是对于每

CF915E Physical Education Lessons|动态开点线段树

动态开点线段树 题目暗示了区间修改,所以我们自然想到了用线段树来维护非工作日的天数. 然而我们再看一下数据范围,天数n的范围是\(1 \le n \le 10^9\),像普通线段树一样预处理显然会爆空间. 天无绝人之路,我们看一下修改个数,$1\le q \le 3 \cdot 10^5 $, 比天数少很多,这也意味着,我们预先处理好的线段树有些节点并没有用 能否优化呢?答案是肯定的,这就是动态开点线段树,顾名思义,我们只要到用某个节点的时候,才分配一个点给它,这样使得我们使用的空间大大减少.其

动态开点线段树

用途 需要建立多棵独立的线段树 线段树维护的值域较大(1e9),但是操作次数较少(1e5) 特征 类似主席树的原理,动态分配每个树节点的位置(lson[],rson[]),每次只更新一条链,但是主席树是建立一颗新的树,动态开点线段树是在一棵树上不断添加节点(还是一棵树) 类似线段树的原理,push_down区间修改,push_up区间查询 例题 1.维护值域较大,线段树区间修改 cf915e https://codeforces.com/contest/915/problem/E 题意: q(3