题目传送门
题意解析:题目就是告诉你n个数,然后有m个询问,每次询问有两个操作,一个是把一段区间内的和输出,一个是把一段区间中的每个数取根号。
My opinion:看到这题就应该知道应该用数据结构维护,不过这个根号该怎么办呢?我们都知道,根号让一个数降下来只需要几次就好了,而根号1或者0都不会变的,我算了一下让10^9变成1或0只需要根号5次就好了,所以,我们可以每次暴力根号,如果一段区间已经全部变成了0或1就不需要更改。那么就是剩下用什么数据结构了,当然是线段树或者分块了,然而有dalao跟我说分块超时?(我没有试过),所以就直接写线段树了。
总结:
1、建树。
2、每次暴力维护,查询依旧。
对,没了,就这么些。
代码:
#include
#include
#include
#include
#include
#include
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define Clear(a,x) memset(a,x,sizeof(a))
#define ll long long
#define INF 2000000000
#define eps 1e-8
#define LEFT root<<1
#define RIGHT root<<1|1
#define Left root<<1,l,mid
#define Right root<<1|1,mid+1,r
using namespace std;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9') f=ch=='-'?-1:f,ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int maxn=100005;
ll sum[maxn<<2];
bool xyf[maxn<<2];
int a[maxn];
int n;
void up(int root){
sum[root]=sum[LEFT]+sum[RIGHT];
xyf[root]=xyf[LEFT]&xyf[RIGHT];
}
void build(int root,int l,int r){
if (l==r){
sum[root]=a[l];
if (a[l]==0||a[l]==1) xyf[root]=1;
else xyf[root]=0;
return;
}
int mid=(l+r)>>1;
build(Left);
build(Right);
up(root);
}
ll find(int root,int l,int r,int x,int y){
if (l>y||x>r) return 0;
if (l==x&&r==y) return sum[root];
int mid=(l+r)>>1;
if (y<=mid) return find(Left,x,y);
else if (x>mid) return find(Right,x,y);
else return find(Left,x,mid)+find(Right,mid+1,y);
}
void change(int root,int l,int r,int x,int y){
if (l>y||x>r) return;
if (l==x&&r==y&&xyf[root]) return;
if (l==r){
a[l]=floor(sqrt(a[l]));
sum[root]=a[l];
if (a[l]==0||a[l]==1) xyf[root]=1;
return;
}
int mid=(l+r)>>1;
if (y<=mid) change(Left,x,y);
else if (x>mid) change(Right,x,y);
else{
change(Left,x,mid);
change(Right,mid+1,y);
}
up(root);
}
int main(){
n=read();
rep(i,1,n) a[i]=read();
build(1,1,n);
int q=read();
while (q--){
int op=read(),l=read(),r=read();
if (op==1) printf("%lld\n",find(1,1,n,l,r));
else change(1,1,n,l,r);
}
return 0;
}
附上AC记录: