#include<stdio.h> #include<iostream> #include<string.h> #include<ctype.h> #include<vector> #include<map> #include<algorithm> #define ls o<<1 #define rs o<<1|1 using namespace std; typedef long long LL; const int N=2e5+10; int n,m; int W,V,O; int ans[N]; struct A { int l,r,flag,num,min; LL sum; }a[1<<19]; struct B { int w,v,o; bool operator < (const B& b)const { return w<b.w; } }b[N]; void pushup(int o) { a[o].sum=a[ls].sum+a[rs].sum; a[o].num=a[ls].num+a[rs].num; a[o].min=min(a[ls].min,a[rs].min); } void pushdown(int o) { if(a[o].flag) { a[ls].sum-=(LL)a[ls].num*a[o].flag; a[rs].sum-=(LL)a[rs].num*a[o].flag; a[ls].flag+=a[o].flag; a[rs].flag+=a[o].flag; a[ls].min-=a[o].flag; a[rs].min-=a[o].flag; a[o].flag=0; return; } } void build(int o,int l,int r) { a[o].l=l; a[o].r=r; a[o].flag=0; if(a[o].l==a[o].r) { scanf("%d",&a[o].sum); a[o].num=1; a[o].min=a[o].sum; return; } int m=(a[o].l+a[o].r)>>1; build(ls,l,m); build(rs,m+1,r); pushup(o); } void del(int o) { if(a[o].l==a[o].r) { a[o].sum=0; a[o].num=0; a[o].min=1e9; return; } pushdown(o); if(a[ls].min<=0)del(ls); if(a[rs].min<=0)del(rs); pushup(o); } void change(int o,int l,int r) { if(l==a[o].l&&r==a[o].r) { a[o].sum-=(LL)W*a[o].num; a[o].flag+=W; a[o].min-=W; if(a[o].min<=0)del(o);//维护区间num return; } pushdown(o); int m=(a[o].l+a[o].r)>>1; if(r<=m)change(ls,l,r); else if(l>m)change(rs,l,r); else { change(ls,l,m); change(rs,m+1,r); } pushup(o); } void find(int o) { if(a[o].l==a[o].r) { ans[O]=a[o].l; return; } pushdown(o); if(a[ls].sum>=V)find(ls); else { V-=a[ls].sum;//逻辑细节不要忘 find(rs); } pushup(o); } int main() { while(~scanf("%d%d",&m,&n)) { build(1,1,n); for(int i=1;i<=m;i++) { scanf("%d%d",&b[i].w,&b[i].v); b[i].o=i; } sort(b+1,b+m+1); for(int i=1;i<=m;i++) { W=b[i].w-b[i-1].w; if(W)change(1,1,n); V=b[i].v; O=b[i].o; if(a[1].sum<V)ans[O]=0; else find(1); } for(int i=1;i<=m;i++)printf("%d ",ans[i]);puts(""); } return 0; } /* 【trick&&吐槽】 1,设计到大数据,都要考虑爆int。 2,思维逻辑要清楚,找最早完成时间有减法不要忘记。 【题意】 有n(2e5)天,每天有一个可以工作的时间。 有m(2e5)个询问,对于每个询问,是关于(准备时间A,完成时间B)的pair,()内的值都是1e6范围 即,我们每天想要工作,要先支出一定的准备时间A。 然后,这天的总时间,减去准备时间,剩下的时间作为我们的有效工作时间。 如果有效工作时间(肯定有效工作时间要>0)之和>=完成时间B,那我们就完工啦。 问最早的完工时间(如果无法完工,输出0) 【类型】 数据结构-线段树 【分析】 这题怎么做呢? 我们发现,我们要求的是——∑(每天时间-准备时间),然而把<=0的筛掉,再求最早的>=完成时间的位置 于是就发现,对于问题的处理,如果我们把准备时间排序,那问题处理起来就好一些了—— 因此,做法出来啦—— 1,我们建一棵线段树,用来存一定日期范围的可行工作时间 2,对于每个询问,把准备时间作为关键字进行排序。 3,对于每个询问,用后准备时间减去前准备时间,这个差值用在线段树上,来维护区间工作时间。 4,然后直接从每个节点,向左走,向右走,处理询问找到最早工作时间就好啦。 细节—— 线段树中要存哪些信息呢? 1,基础的左右端点。 2,做题首先就需要的——区间和sum 3,配合区间减法使用的延迟标记 这个时候,如果我们做区间减法,会怎样呢? 区间和要怎么算?这里有两种做法—— 1,记录区间有效的num值,每次之间sum-=num*W 2,从下区间合并得到。 如果只考虑2,其实还是没法考虑减法对sum的影响。 于是我们还是要记录区间的有效num,同时还要记录区间最小值来维护这个num 然后对于区间减法—— 步骤1,sum-=num*W 步骤2,检查合法性。如果区间最小值<=0,那么就向下删,然后答案从下层pushup得到。 剩下的最后就是关于线段树的pushup()和pushdown()了。 掌握几个原则就好了—— 1,flag只向下pushdown() 2,每次修改一个区间后,我们的修改立即生效,flag只向下延展。 修改包括:区间sum,延迟标记flag,区间最小值min, 【时间复杂度&&优化】 因为每个点最多只删除一次,所以时间复杂度为O((n+m)logn) */