【uoj#228】基础数据结构练习题 线段树+均摊分析
给出一个长度为 n 的序列,支持 m 次操作,操作有三种:区间加、区间开根、区间求和。
n,m,ai≤100000 。
题解
线段树+均摊分析
对于原来的两个数 a 和 b ( a>b ) ,开根后变成 √a 和 √b ,它们的差从 a−b 变成了 √a−√b 。
又有 (√a−√b)(√a+√b)=a−b ,因此开方后的差小于原来差的开方。
而当区间差为 0 或 a=x2,b=x2−1 的 1 时,区间开根就变成了区间减。
因此一个区间开根 loglog(Max−Min) 次后就不需要暴力开根,直接区间减即可。
定义线段树节点势能为 loglog(Max−Min) ,那么每次对 [l,r] 开根就是将所有 l≤x,y≤r ,且势能不为 0 的节点 [x,y] 的势能减 1 ,代价为势能减少总量。
分析区间加操作:只会修改到经过的节点的势能,影响 log 个节点,将这些点的势能恢复为 loglog(Max−Min) 。
因此总的时间复杂度就是总势能量 O((n+mlogn)logloga) 。
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 | #include <cmath> #include <cstdio> #include <algorithm> #define N 100010 #define lson l , mid , x << 1 #define rson mid + 1 , r , x << 1 | 1 using namespace std; typedef long long ll; ll sum[N << 2] , mx[N << 2] , mn[N << 2] , tag[N << 2]; inline void add(ll v , int l , int r , int x) { sum[x] += v * (r - l + 1) , mx[x] += v , mn[x] += v , tag[x] += v; } inline void pushup( int x) { sum[x] = sum[x << 1] + sum[x << 1 | 1]; mx[x] = max(mx[x << 1] , mx[x << 1 | 1]); mn[x] = min(mn[x << 1] , mn[x << 1 | 1]); } inline void pushdown( int l , int r , int x) { if (tag[x]) { int mid = (l + r) >> 1; add(tag[x] , lson) , add(tag[x] , rson); tag[x] = 0; } } inline void build( int l , int r , int x) { if (l == r) { scanf ( "%lld" , &sum[x]) , mx[x] = mn[x] = sum[x]; return ; } int mid = (l + r) >> 1; build(lson) , build(rson); pushup(x); } inline void update( int b , int e , ll a , int l , int r , int x) { if (b <= l && r <= e) { add(a , l , r , x); return ; } pushdown(l , r , x); int mid = (l + r) >> 1; if (b <= mid) update(b , e , a , lson); if (e > mid) update(b , e , a , rson); pushup(x); } inline void change( int b , int e , int l , int r , int x) { if (b <= l && r <= e && mx[x] - (ll) sqrt (mx[x]) == mn[x] - (ll) sqrt (mn[x])) { add((ll) sqrt (mx[x]) - mx[x] , l , r , x); return ; } pushdown(l , r , x); int mid = (l + r) >> 1; if (b <= mid) change(b , e , lson); if (e > mid) change(b , e , rson); pushup(x); } inline ll query( int b , int e , int l , int r , int x) { if (b <= l && r <= e) return sum[x]; pushdown(l , r , x); int mid = (l + r) >> 1; ll ans = 0; if (b <= mid) ans += query(b , e , lson); if (e > mid) ans += query(b , e , rson); return ans; } int main() { int n , m , opt , x , y; ll z; scanf ( "%d%d" , &n , &m); build(1 , n , 1); while (m -- ) { scanf ( "%d%d%d" , &opt , &x , &y); if (opt == 1) scanf ( "%lld" , &z) , update(x , y , z , 1 , n , 1); else if (opt == 2) change(x , y , 1 , n , 1); else printf ( "%lld\n" , query(x , y , 1 , n , 1)); } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步