数据结构(势能线段树)
https://www.luogu.com.cn/problem/P10516
第3题 数据结构 查看测评数据信息
给定两个长度为 n 的序列 a[i] 和 b[i]。有以下三种操作:
1. 给定区间 [l,r] 以及参数 k,t,把区间内满足 a[i]* b[i]<= k 的位置的 a[i] 和 b[i] 分别加上 t。
2. 给定 i 和 x,y,将 a[i] 改为 x,b[i] 改为 y。
3. 查询区间内每个位置 a[i]+b[i] 的和。
输入格式
第一行包含两个整数 n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示 a[i]。
第三行包含 n 个用空格分隔的整数,其中第 i 个数字表示 b[i]。
接下来 m 行每行包含 3 到 5 个整数,表示一个操作,具体如下:
1. 1 l r k t:将区间 [l,r] 进行一操作。
2. 2 i x y:将 a[i] 改为 x,b[i] 改为 y。
3. 3 l r:输出区间 [l,r] 内每个数的和。
1<= n,m<= 1e5,0<= a[i],b[i],k,t,x,y<=1e5
输出格式
若干行,每行表示操作 3 的答案。
输入/输出例子1
输入:
5 5
23 4 3 3 7
54 29 7 1 2
1 1 5 114 1
2 2 7 9
3 1 5
3 1 2
3 3 4
输出:
122
93
18
样例解释
第一次修改后,序列 a[i] 为:{23,4,4,4,8};序列 b[i] 为 {54,29,8,2,3}。
第二次修改后,序列 a[i] 为:{23,7,4,4,8};序列 b[i] 为 {54,9,8,2,3}。
势能线段树概念(重要)
本质上是一种区间暴力修改技术。
考虑普通的区修线段树,我们使用 lazytag 来记录区间修改信息,在访问到当前节点之后再将当前节点的 tag 下传。
然而,有很多信息是不好下传的(不满足交换或结合律),于是我们就无法使用 tag 来记录区修信息。但有的题目每一个操作都有潜在的操作次数上限,我们可以先判断该区间有没有需要操作的节点,有的话遍历到叶子节点进行修改,没有的话返回。
单调操作,区间查询,很容易想到线段树
操作二,三都是板子,我们关注一下操作一
分析一下操作一
看上去是区间修改,但是下放lazy_tag发现很难,对于修改也很难(不知具体改哪一个点),原因就是此操作不满足区间性(区间的每个值同时加减乘除等等吧)。所以考虑单点修改
由于每次操作完
a[i]*b[i] 都变成 (a[i]+t)*(b[i]+t)
我们化简。a[i]*b[i] + a[i]*t + b[i]*t + t*t
注意到最后一项,这样就说明这个值的增长是非常快的,操作次数不超过 sqrt(k) 就可以到上限
但是注意,这里 t不为0,才能增长,否则就是无意义的。
还有一点,要维护一个区间最小值,因为只有<=k的值才能进行修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | #include <bits/stdc++.h> #define int long long using namespace std; const int N=5e5+5; struct node { int Min, sum; }tr[N]; int n, m, x, y, op, a[N], b[N], L, R, k, t; void push_up( int id) { tr[id].sum=tr[id*2].sum+tr[id*2+1].sum; tr[id].Min=min(tr[id*2].Min, tr[id*2+1].Min); } void build( int id, int L, int R) { if (L==R) { tr[id].sum=a[L]+b[L]; tr[id].Min=a[L]*b[L]; return ; } int mid=(L+R)>>1; build(id*2, L, mid); build(id*2+1, mid+1, R); push_up(id); } void change( int id, int L, int R, int x, int y, int k, int t) { if (tr[id].Min>k) return ; if (L==R) { a[L]+=t; b[L]+=t; tr[id].sum=a[L]+b[L]; tr[id].Min=a[L]*b[L]; return ; } int mid=(L+R)>>1, flag=1; if (x<=mid) change(id*2, L, mid, x, y, k, t); if (y>=mid+1) change(id*2+1, mid+1, R, x, y, k, t); push_up(id); } void change2( int id, int L, int R, int x, int v, int v2) { if (L==R) { a[x]=v; b[x]=v2; tr[id].sum=a[L]+b[L]; tr[id].Min=a[L]*b[L]; return ; } int mid=(L+R)>>1; if (x<=mid) change2(id*2, L, mid, x, v, v2); else change2(id*2+1, mid+1, R, x, v, v2); push_up(id); } int ask( int id, int L, int R, int x, int y) { if (x<=L && R<=y) return tr[id].sum; int mid=(L+R)>>1, res=0; if (x<=mid) res+=ask(id*2, L, mid, x, y); if (y>=mid+1) res+=ask(id*2+1, mid+1, R, x, y); return res; } signed main() { scanf ( "%lld%lld" , &n, &m); for ( int i=1; i<=n; i++) scanf ( "%lld" , &a[i]); for ( int i=1; i<=n; i++) scanf ( "%lld" , &b[i]); build(1, 1, n); while (m--) { scanf ( "%lld" , &op); if (op==1) { scanf ( "%lld%lld%lld%lld" , &L, &R, &k, &t); if (t==0) continue ; change(1, 1, n, L, R, k, t); } else if (op==2) { scanf ( "%lld%lld%lld" , &k, &L, &R); change2(1, 1, n, k, L, R); } else { scanf ( "%lld%lld" , &L, &R); printf ( "%lld\n" , ask(1, 1, n, L, R)); } } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南