习题: codevs 2492 上帝造题的七分钟2 解题报告

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了习题: codevs 2492 上帝造题的七分钟2 解题报告相关的知识,希望对你有一定的参考价值。

这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHSK的博客,大神是山东省队队员,他的博客中的题的质量都比我高几个档次);

这是大神MagHSK的解释:因为10^9顶多开5~6次方就成了1了(当然这里的等于是向下取整的)因此对于修改操作,如果某一段不是1或不是0,就暴力修改,如果是1/0就不管他。修改完之后update一下就好了。

题目上说我们给出的是一个可修改(修改的规则就是对要修改的区间内每个数开平方)、可查询(就是求一段区间上所有数字的和)的一个数列。

显然我们直接暴力的效率是很低的,那么我们就采取了线段树——一种神奇的数据结构。

线段树作为可修改可查询的数据结构,基本操作就是点更新、区间更新和区间查询(包括最小值、最大值和区间和)这里因为是解题报告,所以就不跟大家讲基本思路了,直接进入正题,基本思路和代码板子请自己去看。

这里因为是对区间内的数开平方,所以区间更新就有点不大好用了(主要是没法直接标记区间然后开平方,这样会计算错误),然后数据范围也不算大,所以我们就考虑搜索线段树上的每一个在需要update(更新)的区间的所有点进行开方点更新,然后再push_up(往上面推,求出每个线段树上区间的和)上去。这样就可以完成更新任务了。(push_up和change(update)函数代码跟板子上不大一样,all数组就是标记这个区间内的数还值不值得进行开平方操作,显然如果这个区间内的和为1或者0的时候就不用开平方了,change的时候就可以省略了)。

区间查询的操作就是套板子,这个没什么需要思考的。

还是那句老话,要先考虑自己写,然后实在不会了再看题解,总是copy别人的代码自己的水平肯定上升得很慢。

废话不再说,上代码。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cmath>
 4 using namespace std;
 5 typedef long long LL;
 6 const int INF = 1009999999;
 7 int n;
 8 LL sum[500005], A[500005];
 9 bool all[500005];
10 void pu(int now) {
11     sum[now] = sum[now << 1] + sum[now << 1 | 1];
12     all[now] = all[now << 1] && all[now << 1 | 1];
13 }
14 void build(int now, int l, int r) {
15     if (l == r) {
16         sum[now] = A[l];
17         if (sum[now] == 1 || sum[now] == 0)
18             all[now] = true;
19         return;
20     }
21     int mid = (l + r) >> 1;
22     build(now << 1, l, mid);
23     build(now << 1 | 1, mid + 1, r);
24     pu(now);
25 }
26 LL query(int now, int l, int r, int ll, int rr) {
27     if (ll <= l && r <= rr) {
28         return sum[now];
29     }
30     int mid = (l + r) >> 1;
31     LL ret = 0;
32     if (ll <= mid) ret += query(now << 1, l, mid, ll, rr);
33     if (rr > mid) ret += query(now << 1 | 1, mid + 1, r, ll, rr);
34     return ret;
35 }
36 void change(int now, int l, int r, int ll, int rr) {
37     if (all[now]) {
38         return;
39     }
40     if (l == r) {
41         sum[now] = sqrt(sum[now]);
42         if (sum[now] == 1 || sum[now] == 0)
43             all[now] = true;
44         return;
45     }
46     int mid = (l + r) >> 1;
47     if (ll <= mid) change(now << 1, l, mid, ll, rr);
48     if (rr > mid) change(now << 1 | 1, mid + 1, r, ll, rr);
49     pu(now);
50 }
51 int main() {
52     scanf("%d", &n);
53     for (int i = 1; i <= n; ++i)
54         scanf("%lld", A+i);
55     build(1, 1, n);
56     int m, x, y, z;
57     scanf("%d", &m);
58     while (m--) {
59         scanf("%d%d%d", &x, &y, &z);
60         if (y > z) swap(y, z);
61         if (x==1) {
62             printf("%lld\n", query(1, 1, n, y, z));
63         } else {
64             change(1, 1, n, y, z);
65         }
66     }
67     return 0;
68 }

这次解题报告就完结啦,蒟蒻一般是在每周的星期三晚和星期天晚发解题报告,大家就不要在其他时间翻我的博客啦。

以上是关于习题: codevs 2492 上帝造题的七分钟2 解题报告的主要内容,如果未能解决你的问题,请参考以下文章

C++之路进阶——线段树(上帝造题的七分钟 2)

BZOJ3038: 上帝造题的七分钟2

3038: 上帝造题的七分钟2 [线段树 暴力]

bzoj 3038: 上帝造题的七分钟2 线段树||hdu 4027

BZOJ 3038: 上帝造题的七分钟2线段树区间开方问题

P4145 上帝造题的七分钟2 / 花神游历各国