HDU 6315 Naive Operations 【势能线段树】

<题目链接>

题目大意:

给出两个序列,a序列全部初始化为0,b序列为输入值。然后有两种操作,add x y就是把a数组[x,y]区间内全部+1,query x y是查询[x,y]区间内∑[ai/bi]。([ai/bi]代表ai/bi后向下取整)

解题分析:

首先,如果每次+1都暴力更新到每个叶子节点肯定会超时,但是如果不更新到叶子节点又不好维护每个节点对应区间 ∑[ai/bi] 的值,所以我们可以每个节点都维护四个值。sum值代表这个区间每个节点整数部分的所有数之和,lazy进行懒惰标记,避免每次更新到叶子节点,mxa记录该区间内分子的最大值,mnb记录该区间内分母的最小值。之所以要维护这两个最大最小值是因为,当进行区间整体+1操作的时候,如果该区间内最大的分子都小于分母时,说明这个区间在进行+1操作后,并没有对该区间的sum值做出贡献,所以此时就可以将这个+1操作lazy到这个节点;但是如果对区间整体+1后,最大分子大于等于最小分母,此时,就需要继续向下更新,直到找到那个(或者几个)分子大于等于分母的根节点,然后将该节点的sum+1,同时将b值加上原始的brr[l]值(其实我不太明白这一步为什么要这么做,我觉的这步等效于该点的mxa值-该点的mnb值啊,然而这样改了以后超时  T_T)。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 #define Lson rt<<1,l,mid
 7 #define Rson rt<<1|1,mid+1,r
 8 const int M =1e5+10;
 9 
10 int n,m;
11 struct Tree{
12     int lazy,sum;
13     int mxa,mnb;
14 }tr[M<<2];
15 int brr[M];
16 
17 void Pushdown(int rt){
18     if(tr[rt].lazy){
19         int tmp=tr[rt].lazy;
20         tr[rt<<1].lazy+=tmp,tr[rt<<1|1].lazy+=tmp;
21         tr[rt<<1].mxa+=tmp,tr[rt<<1|1].mxa+=tmp;
22         tr[rt].lazy=0;
23     }
24 }
25 void Pushup(int rt){
26     tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
27     tr[rt].mxa=max(tr[rt<<1].mxa,tr[rt<<1|1].mxa);       //维护该区间内分子的最大值和分母的最小值
28     tr[rt].mnb=min(tr[rt<<1].mnb,tr[rt<<1|1].mnb);
29 }
30 void build(int rt,int l,int r){    //初始化
31     tr[rt].lazy=0;
32     if(l==r){
33         tr[rt].mxa=tr[rt].sum=0;     
34         tr[rt].mnb=brr[l];
35         return;
36     }
37     int mid=(l+r)>>1;
38     build(Lson);
39     build(Rson);
40     Pushup(rt);
41 }
42 void update(int rt,int l,int r,int L,int R){      //这个函数是本题的关键
43     if(L<=l&&r<=R){
44         tr[rt].mxa++;
45         if(tr[rt].mxa<tr[rt].mnb){   //如果最大的分子小于最大的分母,说明这个区间内的所有叶子在分子+1之后,没有对sum值多做出贡献,所以我们先将这个操作lazy在这个节点
46             tr[rt].lazy++;
47             return;
48         }
49         if(l==r&&tr[rt].mxa>=tr[rt].mnb){    //如果向下找到了那个分子大于等于分母的叶子节点,那么该节点贡献+1,且将分母加上最初始的防御值,相当于将该真分数的整数部分提出后,再将其变成假分数,方便以后继续统计贡献
50             tr[rt].sum++;
51             tr[rt].mnb+=brr[l];      //我觉的这一步应该等效于tr[rt].mxa-=tr[rt].mnb啊,然而这样改了以后超时
52             return;                   
53         }
54     }
55     Pushdown(rt);
56     int mid=(l+r)>>1;
57     if(L<=mid)update(Lson,L,R);
58     if(R>mid)update(Rson,L,R);
59     Pushup(rt);
60 }
61 int query(int rt,int l,int r,int L,int R){       //查询该区间内所有数的整数部分之和
62     if(L<=l&&r<=R)return tr[rt].sum;      
63     Pushdown(rt);
64     int ans=0;
65     int mid=(l+r)>>1;
66     if(L<=mid)ans+=query(Lson,L,R);
67     if(R>mid)ans+=query(Rson,L,R);
68     return ans;
69 }
70 int main(){
71     while(~scanf("%d%d",&n,&m)){
72         for(int i=1;i<=n;i++)
73             scanf("%d",&brr[i]);
74         build(1,1,n);
75         char op[15];
76         while(m--){
77             int x,y;
78             scanf("%s%d%d",&op,&x,&y);
79             if(op[0]=='a')update(1,1,n,x,y);
80             else printf("%d
",query(1,1,n,x,y));
81         }
82     }
83     return 0;
84 }

2018-10-15

原文地址:https://www.cnblogs.com/00isok/p/9791604.html