花神游历各国(线段树 单修区查和)
花神喜欢步行游历各国,顺便虐爆各地竞赛。花神有一条游览路线,它是线型的,也就是说,所有游历国家呈一条线的形状排列,花神对每个国家都有一个喜欢程度(当然花神并不一定喜欢所有国家)。
每一次旅行中,花神会选择一条旅游路线,它在那一串国家中是连续的一段,这次旅行带来的开心值是这些国家的喜欢度的总和,当然花神对这些国家的喜欢程序并不是恒定的,有时会突然对某些国家产生反感,使他对这些国家的喜欢度δ 变为根号 δ
(可能是花神虐爆了那些国家的 OI,从而感到乏味)。
现在给出花神每次的旅行路线,以及开心度的变化,请求出花神每次旅行的开心值。
输入格式
第一行是一个整数 N,表示有 N 个国家;
第二行有 N 个空格隔开的整数,表示每个国家的初始喜欢度 δ i;
第三行是一个整数 M,表示有 M 条信息要处理;
第四行到最后,每行三个整数 x,l,r,当 x=1 时询问游历国家 l 到 r 的开心值总和,也就是∑δ i,当 x=2 时国家 l 到 r 中每个国家的喜欢度 δ i变为根号下δ i 。
输出格式
每次 x=1时,每行一个整数。表示这次旅行的开心度。
样例
Input
4
1 100 5 5
5
1 1 2
2 1 2
1 1 2
2 2 3
1 1 4
Output
101
11
11
数据范围与提示
对于全部数据,1≤n≤10 5 ,1≤m≤2×10 5,1≤l≤r≤n, 0≤δ i≤10 9 。
注:建议使用 sqrt 函数,且向下取整。
思路: 算是一个线段树单修求区间和的一个板,用en-st+1=t[rt] 会超时,所以要再开一个maxx[]记录区间最大值
AC代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#define ll long long
#define lef st,mid,rt<<1
#define rig mid+1,en,rt<<1|1
#include<algorithm>
using namespace std;
const int N=1e6+10;
ll t[N],a[N],maxx[N];
ll n,m,l,r,ans;
void ud(ll rt)
{
t[rt]=t[rt<<1]+t[rt<<1|1];
maxx[rt]=max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(ll st,ll en,ll rt)
{
if(st==en)
maxx[rt]=t[rt]=a[en];
else
{
ll mid=(st+en)>>1;
build(lef);
build(rig);
ud(rt);
}
}
void update(ll st,ll en,ll rt)
{
//if(en-st+1==t[rt]) return ;
if(maxx[rt]<=1) return;
if(st==en) t[rt]=sqrt(t[rt]),maxx[rt]=t[rt];//
else
{
ll mid=(st+en)>>1;
if(l<=mid)
update(lef);
if(r>mid)
update(rig);
ud(rt);
}
}
ll query(ll st,ll en,ll rt)
{
if(st>r||en<l) return 0;
/*if(en-st+1==t[rt])
return ans+=(min(en,r)-max(st,l)+1);//
//if(st==en) return t[rt];
*/
if(l<=st&&en<=r) return t[rt];//
//ans=0;
ll mid=(st+en)>>1;
if(r<=mid) return query(lef);//全在左区间,返左边
else if(l>mid) return query(rig);//全在右区间,返右边
else//两边都有
{
ll le=query(lef);//
ll ri=query(rig);//
return le+ri;
}
}
int main()
{
ll i,j,k;
scanf("%lld",&n);
for(i=1; i<=n; i++)
scanf("%lld",&a[i]);
build(1,n,1);
scanf("%lld",&m);
while(m--)
{
ll x;
//ans=0;
scanf("%lld%lld%lld",&x,&l,&r);
if(x==1)
printf("%lld\n",query(1,n,1));
else
update(1,n,1);
}
return 0;
}