习题: 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 }
这次解题报告就完结啦,蒟蒻一般是在每周的星期三晚和星期天晚发解题报告,大家就不要在其他时间翻我的博客啦。