题目
n(n<=1e5)个数,m(m<=1e5)次操作,题目保证小于
操作分两种,
1 对[l,r]暴力开根一次
2 询问[l,r]的和
思路来源
傅老师
题解
暴力开根,维护整个区间有没有被开成1
如果被开成1了就不往子区间开了,
注意到1e18的数开个6次,就被开成1了,
复杂度大概是O(6*n)叭
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll a[maxn];
ll sum[maxn*5];
bool ok[maxn*5];//该区间是否整个区间都被开根为1
int cas,n,m;
int op,l,r;
void pushup(int p)
{
sum[p]=sum[p<<1]+sum[p<<1|1];
ok[p]=ok[p<<1]&ok[p<<1|1];
}
void build(int p,int l,int r)
{
sum[p]=ok[p]=0;
if(l==r)
{
sum[p]=a[l];
if(sum[p]==1)ok[p]=1;
else ok[p]=0;
return;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
void update(int p,int l,int r,int ql,int qr)
{
if(ok[p])return;//整个区间都被开成1了
if(l==r)
{
sum[p]=sqrt(sum[p]);
if(sum[p]==1)ok[p]=1;
else ok[p]=0;
return;
}
int mid=(l+r)/2;
if(ql<=mid)update(p<<1,l,mid,ql,qr);
if(qr>mid)update(p<<1|1,mid+1,r,ql,qr);
pushup(p);
}
ll ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return sum[p];
ll ans=0;
int mid=(l+r)/2;
if(ql<=mid)ans+=ask(p<<1,l,mid,ql,qr);
if(qr>mid)ans+=ask(p<<1|1,mid+1,r,ql,qr);
return ans;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
build(1,1,n);
scanf("%d",&m);
printf("Case #%d:\n",++cas);
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&op,&l,&r);
if(l>r)swap(l,r);
if(op==0)update(1,1,n,l,r);
else printf("%lld\n",ask(1,1,n,l,r));
}
puts("");
}
return 0;
}