传送门
整体二分经典题目。
直接整体二分当前区间中的询问操作需要下几场雨。
令当前询问区间是
[
l
,
r
]
[l,r]
[l,r],修改区间是
[
q
l
,
q
r
]
,
m
i
d
=
(
q
l
+
q
r
)
/
2
[ql,qr],mid=(ql+qr)/2
[ql,qr],mid=(ql+qr)/2.
那么我们先计算
[
q
l
,
m
i
d
]
[ql,mid]
[ql,mid]的贡献,将
[
l
,
r
]
[l,r]
[l,r]中达到目标的放在左区间,未达到目标的减去贡献之后放在右区间。
然后做这道题的时候有一个小技巧,我们将最开始的修改区间定成
[
1
,
k
+
1
]
[1,k+1]
[1,k+1],这样k场雨不够的会自动分到k+1。
代码:
#include<bits/stdc++.h>
#define N 300005
#define ll long long
using namespace std;
inline ll read(){
ll ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,m,k,ans[N];
ll bit[N];
struct Upd{int l,r;ll v;}upd[N];
struct Query{int id;ll k;vector<int>pos;}q[N],qtmp[N];
inline int lowbit(int x){return x&-x;}
inline void update(int x,ll v){for(int i=x;i<=m;i+=lowbit(i))bit[i]+=v;}
inline ll query(int x){ll ret=0;for(int i=x;i;i-=lowbit(i))ret+=bit[i];return ret;}
inline void solve(int l,int r,int ql,int qr){
if(l>r)return;
if(ql==qr){
for(int i=l;i<=r;++i)ans[q[i].id]=ql;
return;
}
int mid=ql+qr>>1,hd=l-1,tl=r+1;
for(int i=ql;i<=mid;++i){
if(upd[i].l<=upd[i].r)update(upd[i].l,upd[i].v),update(upd[i].r+1,-upd[i].v);
else update(upd[i].l,upd[i].v),update(1,upd[i].v),update(upd[i].r+1,-upd[i].v);
}
for(int i=l;i<=r;++i){
ll sum=0;
for(int j=0;j<q[i].pos.size();++j){
sum+=query(q[i].pos[j]);
if(sum>=q[i].k)break;
}
if(sum>=q[i].k)qtmp[++hd]=q[i];
else q[i].k-=sum,qtmp[--tl]=q[i];
}
for(int i=ql;i<=mid;++i){
if(upd[i].l<=upd[i].r)update(upd[i].l,-upd[i].v),update(upd[i].r+1,upd[i].v);
else update(upd[i].l,-upd[i].v),update(1,-upd[i].v),update(upd[i].r+1,upd[i].v);
}
for(int i=l;i<=r;++i)q[i]=qtmp[i];
solve(l,hd,ql,mid),solve(tl,r,mid+1,qr);
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;++i)q[read()].pos.push_back(i);
for(int i=1;i<=n;++i)q[i].id=i,q[i].k=read();
k=read();
for(int i=1;i<=k;++i){
int l=read(),r=read();
ll v=read();
upd[i]=(Upd){l,r,v};
}
solve(1,n,1,k+1);
for(int i=1;i<=n;++i){
if(ans[i]<=k)printf("%d\n",ans[i]);
else puts("NIE");
}
return 0;
}