BZOJ 3038: 上帝造题的七分钟2线段树区间开方问题
Posted Angel_Kitty
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 3038: 上帝造题的七分钟2线段树区间开方问题相关的知识,希望对你有一定的参考价值。
3038: 上帝造题的七分钟2
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 1469 Solved: 631
[Submit][Status][Discuss]
Description
XLk觉得《上帝造题的七分钟》不太过瘾,于是有了第二部。
"第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。
Input
第一行一个整数n,代表数列中数的个数。
第二行n个正整数,表示初始状态下数列中的数。
第三行一个整数m,表示有m次操作。
接下来m行每行三个整数k,l,r,k=0表示给[l,r]中的每个数开平方(下取整),k=1表示询问[l,r]中各个数的和。
Output
对于询问操作,每行输出一个回答。
Sample Input
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
7
6
HINT
1:对于100%的数据,1<=n<=100000,1<=l<=r<=n,数列中的数大于0,且不超过1e12。
2:数据不保证L<=R 若L>R,请自行交换L,R,谢谢!
Source
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3038
分析:开方操作的标记是不能合并的,怎么办呢
每个数最大都是1e12,开一次方成了1e6,然后1e3…可以看出来下降的十分迅速,当它到1或者0的时候再开方就没意义了…
所以线段树记录最大值,每次递归左右儿子时若最大值大于1则递归,每次修改区间暴力修改,没几次这个线段树就基本不递归了…
下面给出AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 inline ll read() 5 { 6 ll x=0,f=1; 7 char ch=getchar(); 8 while(ch<‘0‘||ch>‘9‘) 9 { 10 if(ch==‘-‘) 11 f=-1; 12 ch=getchar(); 13 } 14 while(ch>=‘0‘&&ch<=‘9‘) 15 { 16 x=x*10+ch-‘0‘; 17 ch=getchar(); 18 } 19 return x*f; 20 } 21 const ll N=100010; 22 ll a[N]; 23 struct data 24 { 25 ll L,R; 26 ll sum; 27 bool flag; 28 }tree[N<<2]; 29 ll n,m; 30 inline void buildtree(ll l,ll r,ll pos) 31 { 32 tree[pos].L=l; 33 tree[pos].R=r; 34 if(l==r) 35 { 36 tree[pos].sum=a[l]; 37 if(a[l]==1||a[l]==0) 38 tree[pos].flag=1; 39 return; 40 } 41 ll mid=(l+r)/2; 42 buildtree(l,mid,pos*2); 43 buildtree(mid+1,r,pos*2+1); 44 tree[pos].sum=tree[pos*2].sum+tree[pos*2+1].sum; 45 tree[pos].flag=tree[pos*2].flag&tree[pos*2+1].flag; 46 } 47 inline void update(ll l,ll r,ll pos) 48 { 49 if(tree[pos].flag) 50 return; 51 if(tree[pos].L==tree[pos].R) 52 { 53 tree[pos].sum=(ll)sqrt(tree[pos].sum); 54 if(tree[pos].sum==1||tree[pos].sum==0) 55 tree[pos].flag=1; 56 return; 57 } 58 ll mid=(tree[pos].L+tree[pos].R)/2; 59 if(mid>=r) 60 update(l,r,pos*2); 61 else if(mid<l) 62 update(l,r,pos*2+1); 63 else 64 { 65 update(l,mid,pos*2); 66 update(mid+1,r,pos*2+1); 67 } 68 tree[pos].sum=tree[pos*2].sum+tree[pos*2+1].sum; 69 tree[pos].flag=tree[pos*2].flag&tree[pos*2+1].flag; 70 } 71 inline ll Query(ll l,ll r,ll pos) 72 { 73 if(tree[pos].L==l&&tree[pos].R==r) 74 return tree[pos].sum; 75 ll mid=(tree[pos].L+tree[pos].R)/2; 76 if(mid>=r) 77 return Query(l,r,pos*2); 78 else if(mid<l) 79 return Query(l,r,pos*2+1); 80 else 81 return Query(l,mid,pos*2)+Query(mid+1,r,pos*2+1); 82 } 83 int main() 84 { 85 n=read(); 86 for(ll i=1;i<=n;i++) 87 a[i]=read(); 88 buildtree(1,n,1); 89 m=read(); 90 for(ll i=1;i<=m;i++) 91 { 92 ll pos,l,r; 93 pos=read(); 94 l=read(); 95 r=read(); 96 if(l>r) 97 swap(l,r); 98 if(!pos) 99 update(l,r,1); 100 else 101 printf("%lld\n",Query(l,r,1)); 102 } 103 return 0; 104 }
以上是关于BZOJ 3038: 上帝造题的七分钟2线段树区间开方问题的主要内容,如果未能解决你的问题,请参考以下文章