bzoj3211花神游历各国 线段树

来源:互联网 发布:华为双卡切换网络数据 编辑:程序博客网 时间:2024/06/10 14:41

题目链接:戳这里

3211: 花神游历各国

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 4119  Solved: 1504
[Submit][Status][Discuss]

Description

Input

Output

每次x=1时,每行一个整数,表示这次旅行的开心度

Sample Input

4

1 100 5 5

5

1 1 2

2 1 2

1 1 2

2 2 3

1 1 4

Sample Output

101

11

11

HINT

对于100%的数据, n ≤ 100000,m≤200000 ,data[i]非负且小于10^9


Source

一道还算简单的线段树。

看到题目中的区间操作和查询很容易让人想到线段树,但区间开方好像不满足区间加和性质?

所以我们必须修改到每个叶节点上,但这么做就会TLE。

观察数据:因为是向下取整,所以经过几次操作后就会有区间变成1,显然全是1的区间我们就没必要继续修改下去了,这样就能大大减少复杂度。

对于线段树的每个节点,维护一个lazy或区间最大值,当lazy大于6或区间最大值<=1的时候就直接返回。

代码:

#include<bits/stdc++.h>#define maxn 100010 using namespace std;typedef long long LL;int read(){char c;int sum=0,f=1;c=getchar();while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}return sum*f;}int n,ql[maxn];int m;LL sum[maxn*4];int lazy[maxn*4];void pushup(int id){sum[id]=sum[id<<1]+sum[id<<1|1];return;}void build(int id,int l,int r){if(l==r){sum[id]=ql[l];return;}int mid=l+r>>1;build(id<<1,l,mid);build(id<<1|1,mid+1,r);pushup(id);}void update(int id,int ql,int qr,int l,int r){if(l>=ql && r<=qr){lazy[id]++;if(l==r){sum[id]=sqrt(sum[id]);return;}}int mid=(l+r)>>1,lson=(id<<1),rson=(id<<1|1);if(lazy[lson]<6 && ql<=mid) update(lson,ql,qr,l,mid);if(lazy[rson]<6 && qr>mid) update(rson,ql,qr,mid+1,r);pushup(id);return;}LL query(int id,int ql,int qr,int l,int r){if(l>=ql && r<=qr)return sum[id];LL ans=0;    int mid=(l+r)>>1;    if(ql<=mid) ans+=query(id<<1,ql,qr,l,mid);    if(qr>mid) ans+=query(id<<1|1,ql,qr,mid+1,r);    return ans;}int main(){n=read();for(int i=1;i<=n;i++)ql[i]=read();build(1,1,n);m=read();for(int i=1;i<=m;i++){int f=read(),l=read(),r=read();if(f==1) printf("%lld\n",query(1,l,r,1,n));if(f==2) update(1,l,r,1,n);}return 0;}