【线段树】uoj#228. 基础数据结构练习题

get到了标记永久化

sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧。

在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手。于是她的好朋友九条可怜酱给她出了一道题。

给出一个长度为 nn 的数列 AA,接下来有 mm 次操作,操作有三种:

  1. 对于所有的 i[l,r]i∈[l,r],将 AiAi 变成 Ai+xAi+x。
  2. 对于所有的 i[l,r]i∈[l,r],将 AiAi 变成 Ai−−√⌊Ai⌋。
  3. 对于所有的 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,表示操作三。

输出格式

对于每个询问操作,输出一行表示答案。

数据范围

对于所有数据,保证有 1lirin,1Ai,xi1051≤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
",query(1, l, r, 1, n, 0));
96         }
97     }
98     return 0;
99 }

END

原文地址:https://www.cnblogs.com/antiquality/p/9420318.html