[ BZOJ 3038 & 3211 / SPOJ GSS4 ] 上帝造题七分钟2 / 花神游历各国
\(\\\)
\(Description\)
给出一个长度为\(N\)的数列,共进行\(M\)次操作:
-
\(1\ L\ R\):查询\([L,R]\)区间和。
-
\(2\ L\ R\):对\([L,R]\)内的数每个都开平方。
-
\(N\in [1,10^5]\),\(M\in [1,2\times 10^5]\),\(L_i,R_i\in [1,N]\)
\(\\\)
\(Solution\)
- 发现开根操作让数字能快速变小,没几次就变成\(1\)了。
- 于是对下标开树状数组维护区间和,修改时用并查集跳过不用操作的点。
- 具体的写法大致是,每个数记录从当前点开始的第一个不为\(1\)的数的下标,每次操作完检查一下是否需要更新,若需要直接改为下一个位置,再\(find\)以下就找到了下一个需要操作的位置。
\(\\\)
\(Code\)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100010
#define R register
#define gc getchar
using namespace std;
typedef long long ll;
ll n,m,val[N];
struct UFS{
ll f[N];
inline void reset(){for(R ll i=1;i<N;++i)f[i]=i;}
ll find(ll x){return x==f[x]?x:f[x]=find(f[x]);}
inline void merge(ll x,ll y){f[find(x)]=find(y);}
inline bool judge(ll x,ll y){return find(x)==find(y);}
}ufs;
struct BIT{
ll c[N];
inline void reset(){memset(c,0,sizeof(c));}
inline ll lowbit(ll x){return x&-x;}
inline void add(ll p,ll x){for(;p<=n;p+=lowbit(p))c[p]+=x;}
inline ll sum(ll p){ll res=0;for(;p;p-=lowbit(p))res+=c[p];return res;}
}bit;
inline ll rd(){
ll x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
inline void work(){
ufs.reset(); bit.reset();
for(R ll i=1;i<=n;++i) val[i]=rd(),bit.add(i,val[i]);
m=rd();
for(R ll i=1,f,l,r;i<=m;++i){
f=rd(); l=rd(); r=rd();
if(l>r)l^=r^=l^=r;
if(f==1ll) printf("%lld\n",bit.sum(r)-bit.sum(l-1));
else for(R ll j=l,tmp;j<=r;){
bit.add(j,(tmp=sqrt(val[j]))-val[j]); val[j]=tmp;
if(val[j]<=1) ufs.f[j]=j+1; j=ufs.find(j)==j?j+1:ufs.f[j];
}
}
}
int main(){
int tot=0;
while(scanf("%lld",&n)!=EOF) work();
return 0;
}