传送门Can you answer these queries?
题意:线段树,只是区间修改变成 把每个点的值开根号;
思路:对【X,Y】的值开根号,由于最大为 263.可以观察到最多开根号7次即为1,则当根号次数大于等于7时,这段区间值为R-L+1,还有一点是L可能大于R。
以下来自鸟神:(真是强啊)
据这一性质,我们可以得到一种解决方案:对于修改,我们对于区间内的数不全为1的区间更新,直到遇到区间内的数全部为1的区间或者叶子结点为止。这样只要使用线段树,维护区间和的信息即可。
#include <iostream> #include <cstring> #include <algorithm> #include <string> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; const int maxn = 100009; ll a[maxn],sum[maxn*4]; int n,m; void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void buildup(int l,int r,int rt) { if(l==r) { sum[rt]=a[l]; return ; } int mid = (l+r)>>1; buildup(l,mid,rt<<1); buildup(mid+1,r,rt<<1|1); pushup(rt); } void update(int L,int R,int l,int r,int rt) { if(sum[rt]==r-l+1)return ; // 剪枝 if(l==r) { sum[rt]=(int)sqrt(sum[rt]); return; } int mid=(l+r)>>1; if(mid>=L)update(L,R,l,mid,rt<<1); if(R>mid)update(L,R,mid+1,r,rt<<1|1); pushup(rt); } ll query (int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) { return sum[rt]; } ll ans = 0; int mid=(l+r)>>1; if(mid>=L)ans+=query(L,R,l,mid,rt<<1); if(R>mid)ans+=query(L,R,mid+1,r,rt<<1|1); return ans; } int main(){ int cnt=0; while(~scanf("%d",&n)) { printf("Case #%d: ",++cnt); memset(a,0,sizeof(a)); memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } buildup(1,n,1); scanf("%d",&m); for(int i=1;i<=m;i++) { int op,a,b; scanf("%d%d%d",&op,&a,&b); if(a>b)swap(a,b); if(op==1) { ll ans = query(a,b,1,n,1); printf("%lld ",ans); } else { update(a,b,1,n,1); } } printf(" "); } return 0; }