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\n",query(1,1,n,x,y)); 81 } 82 } 83 return 0; 84 }
2018-10-15
作者:is_ok
出处:http://www.cnblogs.com/00isok/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。