【线段树】uoj#228. 基础数据结构练习题
get到了标记永久化
sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧。
在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手。于是她的好朋友九条可怜酱给她出了一道题。
给出一个长度为 nn 的数列 AA,接下来有 mm 次操作,操作有三种:
- 对于所有的 i∈[l,r]i∈[l,r],将 AiAi 变成 Ai+xAi+x。
- 对于所有的 i∈[l,r]i∈[l,r],将 AiAi 变成 ⌊Ai−−√⌋⌊Ai⌋。
- 对于所有的 i∈[l,r]i∈[l,r],询问 AiAi 的和。
作为一个不怎么熟练的初学者,sylvia 想了好久都没做出来。而可怜酱又外出旅游去了,一时间联系不上。于是她决定向你寻求帮助:你能帮她解决这个问题吗。
输入格式
第一行两个数:n,mn,m。
接下来一行 nn 个数 AiAi。
接下来 mm 行中,第 ii 行第一个数 titi 表示操作类型:
若 ti=1ti=1,则接下来三个整数 li,ri,xili,ri,xi,表示操作一。
若 ti=2ti=2,则接下来三个整数 li,rili,ri,表示操作二。
若 ti=3ti=3,则接下来三个整数 li,rili,ri,表示操作三。
输出格式
对于每个询问操作,输出一行表示答案。
数据范围
对于所有数据,保证有 1≤li≤ri≤n,1≤Ai,xi≤1051≤li≤ri≤n,1≤Ai,xi≤105
时间限制:1s1s
空间限制:256MB
题目分析
考虑只有区间开方和区间求和的操作,那么注意到开根号几次后数就变为1了,于是可以暴力做下去(或者分块弄一弄?)。
然而问题麻烦在于还结合了区间加的操作,于是会出现形如898989->232323->898989的极端数据卡掉暴力区间下爬的方法。
注意到若区间最大值等于区间最小值时就等于区间赋值(或者区间减)操作,并且复杂度最坏的情况只会在最大值与最小值相差1的时候发生。
那么相当于只要在区间开方时特判一下就好了。
(从别人博客get到了永久化标记的写法(据说常数挺小?))
复杂度证明:UOJ 228 基础数据结构练习题
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const int maxn = 100035; 4 5 struct node 6 { 7 ll sum,mx,mn,tag; 8 }f[maxn<<2]; 9 int n,m; 10 11 int read() 12 { 13 char ch = getchar(); 14 int num = 0; 15 bool fl = 0; 16 for (; !isdigit(ch); ch = getchar()) 17 if (ch=='-') fl = 1; 18 for (; isdigit(ch); ch = getchar()) 19 num = (num<<1)+(num<<3)+ch-48; 20 if (fl) num = -num; 21 return num; 22 } 23 void pushup(int rt, int lens) 24 { 25 f[rt].sum = f[rt<<1].sum+f[rt<<1|1].sum+1ll*f[rt].tag*lens; 26 f[rt].mx = std::max(f[rt<<1].mx, f[rt<<1|1].mx)+f[rt].tag; 27 f[rt].mn = std::min(f[rt<<1].mn, f[rt<<1|1].mn)+f[rt].tag; 28 } 29 void tagAdd(int rt, int lens, ll c) 30 { 31 f[rt].tag += c, f[rt].mn += c, f[rt].mx += c, f[rt].sum += 1ll*lens*c; 32 } 33 void build(int rt, int l, int r) 34 { 35 if (l==r){ 36 f[rt].sum = f[rt].mx = f[rt].mn = read(); 37 return; 38 } 39 int mid = (l+r)>>1; 40 build(rt<<1, l, mid), build(rt<<1|1, mid+1, r); 41 pushup(rt, r-l+1); 42 } 43 void updateAdd(int rt, int L, int R, int l, int r, ll c) 44 { 45 if (L <= l&&r <= R){ 46 tagAdd(rt, r-l+1, c); 47 return; 48 } 49 int mid = (l+r)>>1; 50 if (L <= mid) updateAdd(rt<<1, L, R, l, mid, c); 51 if (R > mid) updateAdd(rt<<1|1, L, R, mid+1, r, c); 52 pushup(rt, r-l+1); 53 } 54 void updateSqrt(int rt, int L, int R, int l, int r, ll tag) 55 { 56 if (L <= l&&r <= R){ 57 ll s = sqrt(f[rt].mn+tag)+1, t = sqrt(f[rt].mx+tag); 58 if ((f[rt].mx==f[rt].mn)||(f[rt].mx==f[rt].mn+1&&s==t)){ 59 tagAdd(rt, r-l+1, s-1-f[rt].mn-tag); //这一步是用来保证复杂度的 60 return; 61 } 62 } 63 int mid = (l+r)>>1; 64 if (L <= mid) updateSqrt(rt<<1, L, R, l, mid, tag+f[rt].tag); 65 if (R > mid) updateSqrt(rt<<1|1, L, R, mid+1, r, tag+f[rt].tag); 66 pushup(rt, r-l+1); 67 } 68 ll query(int rt, int L, int R, int l, int r, ll tag) 69 { 70 if (L <= l&&r <= R) 71 return f[rt].sum+1ll*tag*(r-l+1); 72 int mid = (l+r)>>1; 73 ll ret = 0; 74 if (L <= mid) ret = query(rt<<1, L, R, l, mid, tag+f[rt].tag); 75 if (R > mid) ret += query(rt<<1|1, L, R, mid+1, r, tag+f[rt].tag); 76 return ret; 77 } 78 int main() 79 { 80 n = read(), m = read(); 81 build(1, 1, n); 82 while (m--) 83 { 84 int opt = read(); 85 if (opt==1){ 86 int l = read(), r = read(), c = read(); 87 updateAdd(1, l, r, 1, n, c); 88 } 89 if (opt==2){ 90 int l = read(), r = read(); 91 updateSqrt(1, l, r, 1, n, 0); 92 } 93 if (opt==3){ 94 int l = read(), r = read(); 95 printf("%lld\n",query(1, l, r, 1, n, 0)); 96 } 97 } 98 return 0; 99 }
END