2492 上帝造题的七分钟 2
XLk觉得《上帝造题的七分钟》不太过瘾,于是有了第二部。
"第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。
第一行一个整数n,代表数列中数的个数。
第二行n个正整数,表示初始状态下数列中的数。
第三行一个整数m,表示有m次操作。
接下来m行每行三个整数k,l,r,k=0表示给[l,r]中的每个数开平方(下取整),k=1表示询问[l,r]中各个数的和。
UPD:注意数据中有可能l>r,所以遇到这种情况请交换l和r。
对于询问操作,每行输出一个回答。
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
19
7
6
对于30%的数据,1<=n,m<=1000,数列中的数不超过32767。
对于100%的数据,1<=n,m<=100000,1<=l,r<=n,数列中的数大于0,且不超过1e12。
注意l有可能大于r,遇到这种情况请交换l,r。
来源:Nescafe 20
分析 Analysis
这道题被自己搞的无比复杂= =
再次死在追求正解的道路上= =
根据hzwer的方法:单点暴力修改,区间求和
(也就区间求和是线段树特性,单点暴力= =)
本来以为必爆,但是分析复杂度发现 --不会爆qwq
所以其实 hzwer 的优化就加了一个:
对于一段连续区间,如果都是1,显然没必要继续开方,直接跳过
那么在线段树版本中,如果一个子树整个的都是1,那么修改的时候就可以直接跳过了
------------------------------------------------------------------------
然而据说正解是 树状数组+并查集 ?
不过其实我不应该纠结正解 --能做而且不会爆就好
毕竟不知道死在正解上多少次了
树状数组+并查集 => 单点修改+区间求和+小优化
小优化即上文所说的跳过11111111111111...
代码 Code
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #define LL long long 5 #define mid (L+R)/2 6 #define lc (rt<<1) 7 #define rc (rt<<1|1) 8 #define maxn 1000000 9 using namespace std; 10 11 struct node{ 12 LL sum,flag; 13 }Tree[maxn*4]; 14 15 int n,m;LL a,b,c; 16 17 void maintain(int rt){ 18 Tree[rt].sum = Tree[lc].sum + Tree[rc].sum; 19 Tree[rt].flag = Tree[lc].flag&Tree[rc].flag; 20 } 21 22 void build(int rt,int L,int R){ 23 if(L == R){ 24 scanf("%lld",&Tree[rt].sum); 25 if(Tree[rt].sum == 1 || Tree[rt].sum == 0) Tree[rt].flag = 1; 26 else Tree[rt].flag = 0; 27 }else{ 28 build(lc,L,mid); 29 build(rc,mid+1,R); 30 31 maintain(rt); 32 } 33 } 34 35 void modify(int rt,int L,int R,int qL,int qR){ 36 37 if(Tree[rt].flag) return; 38 39 if(L == R){ 40 Tree[rt].sum = (LL)sqrt(Tree[rt].sum); 41 if(Tree[rt].sum == 1 || Tree[rt].sum == 0) Tree[rt].flag = 1; 42 }else{ 43 44 if(qL <= mid) modify(lc,L,mid,qL,qR); 45 if(qR > mid) modify(rc,mid+1,R,qL,qR); 46 47 maintain(rt); 48 } 49 } 50 51 LL query(int rt,int L,int R,int qL,int qR){ 52 53 // if(Tree[rt].flag) return (R-L+1); 54 55 if(qL <= L && R <= qR){ 56 return Tree[rt].sum; 57 }else{ 58 LL ans = 0; 59 if(qL <= mid) ans += query(lc,L,mid,qL,qR); 60 if(qR > mid) ans += query(rc,mid+1,R,qL,qR); 61 62 return ans; 63 } 64 } 65 66 int main(){ 67 scanf("%d",&n); 68 build(1,1,n); 69 scanf("%d",&m); 70 71 for(int i = 1;i <= m;i++){ 72 scanf("%lld%lld%lld",&a,&b,&c); 73 if(b > c) swap(b,c); 74 if(a) printf("%lld ",query(1,1,n,b,c)); 75 else modify(1,1,n,b,c); 76 } 77 78 return 0; 79 }
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #define maxn 1000000 5 #define LL long long 6 #define lowbit(x) (-x&x) 7 using namespace std; 8 9 LL Tree[maxn],n,m,a,b,c,cnt,arr[maxn]; 10 void add(LL pos,LL val){ 11 while(pos <= n){ 12 Tree[pos] += val; 13 pos += lowbit(pos); 14 } 15 }LL sum(LL pos){ 16 LL ans = 0; 17 if(!pos) return ans; 18 while(pos > 0){ 19 ans += Tree[pos]; 20 pos -= lowbit(pos); 21 }return ans; 22 } 23 24 LL pre[maxn]; 25 LL find(LL x){ 26 if(!pre[x]) return x; 27 else{ 28 pre[x] = find(pre[x]); 29 return pre[x]; 30 } 31 }void unite(LL u,LL v){ 32 if(find(u)!=find(v)) pre[find(u)] = find(v); 33 } 34 35 int main(){ 36 scanf("%lld",&n); 37 for(LL i = 1;i <= n;i++){ 38 scanf("%lld",&arr[i]); 39 add(i,arr[i]); 40 // for(int i = 1;i <= n;i++){ 41 // printf("%lld ",sum(i)); 42 // }cout << endl; 43 } 44 45 scanf("%lld",&m); 46 for(LL i = 1;i <= m;i++){ 47 scanf("%lld%lld%lld",&a,&b,&c); 48 49 if(b > c) swap(b,c); 50 if(a){ 51 printf("%lld ",sum(c)-sum(b-1)); 52 }else{ 53 for(LL j = find(b);j <= c;j = find(j+1)){ 54 if(j == 0) break; 55 add(j,-arr[j]); 56 arr[j] = (LL)sqrt(arr[j]); 57 add(j,arr[j]); 58 if(arr[j] == 1) pre[j] = j+1; 59 } 60 } 61 } 62 63 return 0; 64 }