上帝造题的七分钟2 / 花神游历各国

洛咕

双倍经验 洛咕

题意:给定长度为\(N(N<=100000)\)的序列,操作一:将区间\([l,r]\)中的每个数都开方(向下取整),操作二:查询区间和.

分析:题解真妙啊...因为题目保证了序列中最大的数不会超过\(10^{12}\),而\(10^{12}\)开方并且向下取整的话只要6次就能开到只剩1(\(10^{12}->10^6->10^3->31->5->2->1\)),而对于开到1的数显然对于操作一可以忽略,所以我们直接对于不为1的数暴力开方即可.

具体来说,我们建一棵线段树,每个节点记录maxn和sum值,即区间最大值和区间和,对于操作一,如果该节点的maxn>1就暴力开方修改,否则可以忽略.对于操作二就很普通了.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline ll read(){
    ll x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=100005;
ll a[N];
struct xd_tree{
	int l,r;ll maxn,sum;
}t[N*4];
inline void build(int p,int l,int r){
	t[p].l=l;t[p].r=r;
	if(l==r){t[p].maxn=t[p].sum=a[l];return;}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);build((p<<1)|1,mid+1,r);
	t[p].sum=t[p<<1].sum+t[(p<<1)|1].sum;
	t[p].maxn=max(t[p<<1].maxn,t[(p<<1)|1].maxn);
}
inline void change(int p,int l,int r){
    if(t[p].l==t[p].r){
        t[p].maxn=t[p].sum=sqrt(t[p].sum);
        return;
    }   
    int mid=(t[p].l+t[p].r)>>1;
    if(l<=mid&&t[p<<1].maxn>1)change(p<<1,l,r);
    if(r>mid&&t[(p<<1)|1].maxn>1)change((p<<1)|1,l,r);
    t[p].sum=t[p<<1].sum+t[(p<<1)|1].sum;
	t[p].maxn=max(t[p<<1].maxn,t[(p<<1)|1].maxn);
}
inline ll ask(int p,int l,int r){
	if(l<=t[p].l&&r>=t[p].r)return t[p].sum;
	int mid=(t[p].l+t[p].r)>>1;
	ll val=0;
	if(l<=mid)val+=ask(p<<1,l,r);
	if(r>mid)val+=ask((p<<1)|1,l,r);
	return val;
}
int main(){
	int n=read();for(int i=1;i<=n;++i)a[i]=read();	
	build(1,1,n);int m=read();
	while(m--){
		int k=read(),l=read(),r=read();
		if(l>r)swap(l,r);
		if(k==0)change(1,l,r);
		if(k==1)printf("%lld\n",ask(1,l,r));
	}
    return 0;
}

posted on 2019-08-13 15:09  PPXppx  阅读(92)  评论(0编辑  收藏  举报