题意
给出一个序列,有两种操作:对[l,r]的数开方(下取整),求[l,r]的区间和
不保证给出的区间[x, y] 有x <= y ,如果x>y,请交换x,y。
n,m<=100000
$\sum_{i=1}^{n}a_{i} \leq 1e18$
题解
上帝造题的7分钟2很早之前写过了,发现双倍经验而且这道题也有点意思,就写了这篇博客。
区间求和+区间修改显然线段树可以,那么怎样区间开方,这个其实是不好维护的。考虑暴力——单点修改,每个点最多被修改10次左右吧就可以变成1就不需要被改了,那我们就需要保证不去不需要被修改的点,这个用区间和与区间长度就可以看出了。
#include<cstdio> #include<cstring> #include<cmath> using namespace std; #define ls rt<<1 #define rs rt<<1|1 #define mid ((l+r)>>1) const int maxn=100005; int n,m,a_l,a_r; long long sum[maxn<<2],a[maxn]; void swap(int &x,int &y){ int t=x; x=y; y=t; } void update(int rt){ sum[rt]=sum[ls]+sum[rs]; } void build(int rt,int l,int r){ if(l==r){sum[rt]=a[l];return ;} build(ls,l,mid);build(rs,mid+1,r); update(rt); } void modify(int rt,int l,int r){ if(sum[rt]<=r-l+1) return; if(l==r){sum[rt]=sqrt(sum[rt]);return ;} if(a_l<=mid) modify(ls,l,mid); if(mid<a_r) modify(rs,mid+1,r); update(rt); } long long query(int rt,int l,int r){ if(a_l<=l&&r<=a_r) return sum[rt]; long long ans=0; if(a_l<=mid) ans+=query(ls,l,mid); if(mid<a_r) ans+=query(rs,mid+1,r); return ans; } int main(){ int num=0; while(scanf("%d",&n)!=EOF){ printf("Case #%d:\n",++num); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,1,n); scanf("%d",&m); for(int i=1;i<=m;i++){ int ty; scanf("%d%d%d",&ty,&a_l,&a_r); if(a_l>a_r) swap(a_l,a_r); if(ty==0) modify(1,1,n); else printf("%lld\n",query(1,1,n)); } } }View Code