这是一道板子题,实名制艹
整体二分的板子,我们在这里主要讲讲算法流程
我们把所有的询问放到一起,二分一个时间点
对于所有任务,如果可以完成目标,我们就放到左区间里面,不然放到右区间
时间像二分的一样
[
l
,
r
]
−
>
[
l
,
m
i
d
]
+
[
m
i
d
+
1
,
r
]
[l,r]->[l,mid]+[mid+1,r]
[l,r]−>[l,mid]+[mid+1,r]
直到
l
=
=
r
l==r
l==r我们确定了答案,我们把这时队列里所有任务的答案更新一下即可
还是比较好懂的,整体二分的板子还有一题P3332 [ZJOI2013]K大数查询
不过那题我还是比较喜欢树状数组套主席树的写法主要是懒,抽空补了吧
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef long long lll;
const int maxn = 300007;
const int INF = 2147483647;
long long tr[maxn],val[maxn];
int n,m,k;
vector<int>cont[maxn];
int ans[maxn];
int ll[maxn],rr[maxn],num[maxn],t;
int lt[maxn],rt[maxn],pos[maxn],want[maxn];
int lowbit(int x){return x&-x;}
void jb(int x,int num){
for(;x<=m;x+=lowbit(x))
tr[x]+=num;
}
lll que(int x){
lll res=0;
for(;x;x-=lowbit(x))res+=tr[x];
return res;
}
void change(int l,int r,int num){
if(l<=r){
jb(l,num),jb(r+1,-num);
}
else{
jb(l,num),jb(m+1,-num);
jb(1,num),jb(r+1,-num);
}
}
void query(int l,int r,int p1,int p2){
if(l>r)return;if(p1>p2)return;
if(l==r){
for(int i=p1;i<=p2;i++){
ans[pos[i]]=l;
}
return ;
}
int mid=(l+r)>>1;
while(t<mid)++t,change(ll[t],rr[t],num[t]);
while(t>mid)change(ll[t],rr[t],-num[t]),t--;
for(int i=p1;i<=p2;i++){
val[pos[i]]=0;
for(int j=0;j<cont[pos[i]].size();j++){
val[pos[i]]+=que(cont[pos[i]][j]);
if(val[pos[i]]>=want[pos[i]])break;
}
}
int ls=0,rs=0;
for(int i=p1;i<=p2;i++){
if(val[pos[i]]>=want[pos[i]]){
lt[++ls]=pos[i];
}
else rt[++rs]=pos[i];
}
int mid2=ls;
for(int i=1;i<=ls;i++)pos[i+p1-1]=lt[i];
for(int i=1;i<=rs;i++)pos[i+p1+ls-1]=rt[i];
query(l,mid,p1,p1+mid2-1);
query(mid+1,r,p1+mid2,p2);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x;
scanf("%d",&x);
cont[x].push_back(i);
}
for(int i=1;i<=n;i++)
scanf("%d",&want[i]),pos[i]=i;
scanf("%d",&k);
for(int i=1;i<=k;i++){
scanf("%d%d%d",&ll[i],&rr[i],&num[i]);
}
++k,ll[k]=1,rr[k]=m;num[k]=INF;
query(1,k,1,n);
for(int i=1;i<=n;i++){
if(ans[i]==k){
printf("NIE\n");
}
else printf("%d\n",ans[i]);
}
return 0;
}