线段树+二分(笔试题第一次遇到线段树相关的,记录下)
博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~
https://www.cnblogs.com/chenxiwenruo/p/9609857.html
特别不喜欢那些随便转载别人的原创文章又不给出链接的
所以不准偷偷复制博主的博客噢~~
题意:
有1,2,~n层香槟的塔,如果第i层香槟满了,那么多余的水会往下一层(i+1)流去
现给两种操作:
2 x v 即往第x层倒入体积为v的香槟
1 x 查询第x层装了多少体积的香槟
输入:
n m
n个数ai,表示每层塔的容量
接下来m行即为m个操作
当1 x的时候,输出对应的第x层容量
1<=n,m<=200000
由于m范围,所以暴力肯定是不可能的,思路是用线段树,这样时间复杂度就是NlgN
构建一棵线段树,叶子节点即为1~n层的剩余容量。
当往x层添加v容量的香槟,在[x,n]区间二分查找k,使得[x,k-1]的容量小于v,但[x,k]的容量>=v
然后这样就把[x,k-1]区间清零,叶子节点k的容量减去v-[x,k-1]
对于查询的时候,只要查询叶子节点的,结果为a[i]-left_sum[x,x]
#include <iostream> #include <stdio.h> #include <string.h> #define lson rt<<1,L,mid #define rson rt<<1|1,mid+1,R using namespace std; const int maxn=1000005; int n,m; long long a[200000+5]; struct Node{ long long left_sum; bool lazy; }tree[maxn<<4]; void pushUp(int rt){ tree[rt].left_sum=tree[rt<<1].left_sum+tree[rt<<1|1].left_sum; } void pushDown(int rt){ if(tree[rt].lazy){ tree[rt<<1].left_sum=0; tree[rt<<1|1].left_sum=0; tree[rt<<1].lazy=tree[rt<<1|1].lazy=true; tree[rt].lazy=false; } } void build(int rt,int L,int R){ if(L==R){ tree[rt].left_sum=a[L]; tree[rt].lazy=false; return ; } int mid=(L+R)>>1; build(lson); build(rson); pushUp(rt); } void update(int rt,int L,int R,int l,int r,long long val){ if(l<=L&&R<=r){ if(val==0){ tree[rt].left_sum=0; tree[rt].lazy=true; } else{ tree[rt].left_sum-=val; //pushUp(rt); } return; } pushDown(rt); int mid=(L+R)>>1; if(l<=mid) update(lson,l,r,val); if(r>mid) update(rson,l,r,val); pushUp(rt); } long long query(int rt,int L,int R,int l,int r){ long long ret=0; if(l>r) return 0; if(l<=L&&R<=r){ return tree[rt].left_sum; } pushDown(rt); int mid=(L+R)>>1; if(l<=mid) ret+=query(lson,l,r); if(r>mid) ret+=query(rson,l,r); return ret; } /* 二分查找k 使得left_sum[x,k-1]<v<=left_sum[x,k] 若left_sum[x,n]<v 则返回n+1 */ int binary_searchs(int x,int n,int v){ int l=x-1,r=n+1; int mid; while(l<r-1){ mid=(l+r)>>1; //printf("%d %d %d\n",l,mid,r); if(query(1,1,n,l,mid)>=v) r=mid; else{ l=mid; } } return r; } int main() { int x; long long v; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); } build(1,1,n); int id; for(int i=0;i<m;i++){ scanf("%d",&id); if(id==2){ scanf("%d %lld",&x,&v); int k=binary_searchs(x,n,v); int left=v-query(1,1,n,x,k-1); //printf("k:%d left:%d\n",k,left); //下面当时写成了了update(1,1,n,1,k-1,0) //导致只过了60%的样例,不过没想到数据这么弱,这么个大错误还能过60%样例 if(k>x) update(1,1,n,x,k-1,0); //将[x,k-1]区间清零,即剩余容量为0 if(k<=n) update(1,1,n,k,k,left); //将[k,k]区间剩余容量-left } else{ scanf("%d",&x); long long ans=query(1,1,n,x,x); printf("%lld\n",a[x]-ans); } } return 0; }