题目背景
XLk觉得《上帝造题的七分钟》不太过瘾,于是有了第二部。
题目描述
"第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。
输入输出格式
输入格式:
第一行一个整数 nn ,代表数列中数的个数。
第二行 nn 个正整数,表示初始状态下数列中的数。
第三行一个整数 mm ,表示有 mm 次操作。
接下来 mm 行每行三个整数k,l,r
,
k=0
表示给 [l,r][l,r] 中的每个数开平方(下取整)k=1
表示询问 [l,r][l,r] 中各个数的和。
数据中有可能 l>rl>r ,所以遇到这种情况请交换l和r。
输出格式:
对于询问操作,每行输出一个回答。
输入输出样例
说明
对于30%的数据, 1le n,mle 10001≤n,m≤1000 ,数列中的数不超过 3276732767 。
对于100%的数据, 1 le n,m le 1000001≤n,m≤100000 , 1 le l,r le n1≤l,r≤n ,数列中的数大于 00 ,且不超过 10^{12}1012 。
注意l有可能大于r,遇到这种情况请交换l,r。
一类经典的线段树套路:虽然区间打标机是没法实现的,但是介于每个单点被操作很少的次数之后就会不变,这个时候就可以暴力修改合并。
对于这个题来说: 10^12 -> 10^6 -> 10^3 -> 30+ -> 5 -> 2 -> 1,暴力修改就好啦
#include<bits/stdc++.h> #define ll long long using namespace std; #define lc (o<<1) #define mid (l+r>>1) #define rc ((o<<1)|1) const int maxn=100005; int n,num[maxn*4],le,ri,opt,m; ll sum[maxn*4],ans; inline void maintain(int o){ sum[o]=sum[lc]+sum[rc],num[o]=num[lc]+num[rc];} void build(int o,int l,int r){ if(l==r){ scanf("%lld",sum+o),num[o]=sum[o]>1; return;} build(lc,l,mid),build(rc,mid+1,r); maintain(o); } void update(int o,int l,int r){ if(l==r){ sum[o]=sqrt(sum[o]+0.5),num[o]=sum[o]>1; return;} if(le<=mid&&num[lc]) update(lc,l,mid); if(ri>mid&&num[rc]) update(rc,mid+1,r); maintain(o); } void query(int o,int l,int r){ if(l>=le&&r<=ri){ ans+=sum[o]; return;} if(le<=mid) query(lc,l,mid); if(ri>mid) query(rc,mid+1,r); } int main(){ scanf("%d",&n); build(1,1,n); scanf("%d",&m); while(m--){ scanf("%d%d%d",&opt,&le,&ri); if(le>ri) swap(le,ri); if(opt) ans=0,query(1,1,n),printf("%lld ",ans); else update(1,1,n); } return 0; }