HDU4027 Can you answer these queries? —— 线段树 区间修改
题目链接:https://vjudge.net/problem/HDU-4027
You are asked to answer the queries that the sum of the endurance of a consecutive part of the battleship line.
Notice that the square root operation should be rounded down to integer.
InputThe input contains several test cases, terminated by EOF.
For each test case, the first line contains a single integer N, denoting there are N battleships of evil in a line. (1 <= N <= 100000)
The second line contains N integers Ei, indicating the endurance value of each battleship from the beginning of the line to the end. You can assume that the sum of all endurance value is less than 2 63.
The next line contains an integer M, denoting the number of actions and queries. (1 <= M <= 100000)
For the following M lines, each line contains three integers T, X and Y. The T=0 denoting the action of the secret weapon, which will decrease the endurance value of the battleships between the X-th and Y-th battleship, inclusive. The T=1 denoting the query of the commander which ask for the sum of the endurance value of the battleship between X-th and Y-th, inclusive.
OutputFor each test case, print the case number at the first line. Then print one line for each query. And remember follow a blank line after each test case.Sample Input
10 1 2 3 4 5 6 7 8 9 10 5 0 1 10 1 1 10 1 1 5 0 5 8 1 4 8
Sample Output
Case #1: 19 7 6
题解:
1.因为对区间的操作是:对每个数进行开根,所以不能像以前加减操作那样,也直接对区间的和进行操作(因为:a+b=sum 不能推出 根号(a)+根号(b) = 根号(a+b))。
2.根据上一点,所以在线段树中,我们只能对一段区间一直更新到每一个元素,然后再push_up求和。但是,如果每个操作都如此,那复杂度得多大?直接用数组维护比线段树更快,那要线段树何用?
3.再回看题目,数据最大为2^64,然后操作是对其开根,我们可以知道对1开根还是1,即当一个值是1时,我们不需要对其进行操作了。推广到一个区间:如果这个区间的所有元素都为1,那么我们也不需要再对这个区间进行操作。那么我们对2^64逐次开根:2^32 2^16 2^8 2^8 2^4 2^2 2^1 1,可以发现,一个数最多只需开7次根就会变成1,操作量很小了。所以:利用线段树进行维护,当一段区间的所有元素都为1,那么我们直接返回;否则更新至叶子结点(深入到每个元素)。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <vector> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const double EPS = 1e-8; 15 const int INF = 2e9; 16 const LL LNF = 2e18; 17 const int MAXN = 1e5+10; 18 19 LL sum[MAXN<<2]; 20 21 void push_up(int u) 22 { 23 sum[u] = sum[u*2] + sum[u*2+1]; 24 } 25 26 void build(int u, int l, int r) 27 { 28 if(l==r) 29 { 30 scanf("%lld", &sum[u]); 31 return; 32 } 33 34 int mid = (l+r)>>1; 35 build(u*2, l, mid); 36 build(u*2+1, mid+1, r); 37 push_up(u); 38 } 39 40 void attack(int u, int l, int r, int x, int y) 41 { 42 if(l==r) 43 { 44 sum[u] = (LL)sqrt(sum[u]); 45 return; 46 } 47 //如果这段区域的每个值都为1,那么就无需再执行操作了 48 if(x<=l && r<=y && sum[u]==1LL*(r-l+1)) return; 49 50 int mid = (l+r)>>1; 51 if(x<=mid) attack(u*2, l, mid, x, y); 52 if(y>=mid+1) attack(u*2+1, mid+1, r, x, y); 53 push_up(u); 54 } 55 56 LL query(int u, int l, int r, int x, int y) 57 { 58 if(x<=l && r<=y) 59 return sum[u]; 60 61 LL ret = 0; 62 int mid = (l+r)>>1; 63 if(x<=mid) ret += query(u*2, l, mid, x, y); 64 if(y>=mid+1) ret += query(u*2+1, mid+1, r, x, y); 65 return ret; 66 } 67 68 int main() 69 { 70 int n, m, kase = 0; 71 while(scanf("%d", &n)!=EOF) 72 { 73 build(1, 1, n); 74 scanf("%d", &m); 75 printf("Case #%d:\n", ++kase); 76 for(int i = 1; i<=m; i++) 77 { 78 LL op, x, y; 79 scanf("%lld%lld%lld", &op, &x, &y); 80 int xx = min(x, y), yy = max(x, y); 81 if(op==0) attack(1, 1, n, xx, yy ); 82 else printf("%lld\n", query(1, 1, n, xx, yy) ); 83 } 84 printf("\n"); 85 } 86 }