【瞎口胡】整体二分
整体二分是处理多个相似询问的利器。
例题 [POI2011]MET-Meteors
题意
有一个 \(n\) 个节点的环,每个节点都有一个所属国家。共有 \(n\) 个国家,第 \(i\) 个国家的陨石需求量为 \(p_i\)。
现在有 \(k\) 场陨石雨,每次陨石雨会落在 \([l,r]\) 区间内每一个节点上,并使这些节点的陨石数量增加 \(a\)。
询问每个国家至少在第几次陨石雨之后才能达到需求量,或者指出这不可能。
\(1 \leq n,m ,k \leq 3 \times 10^5,1 \leq p_i,a_i \leq 10^9\)
题解
先考虑一个询问是怎么样的。直接二分:如果 \([l,mid]\) 的陨石雨落下来之后达到了需求量,那答案一定 \(\leq mid\),清除 \([l,mid]\) 的陨石雨并递归 \([l,mid]\);否则不清除,递归 \([mid+1,r]\)。
\(n\) 个询问也是一样的。二分,把 \([l,mid]\) 的陨石雨落下来之后可以达到需求量的询问往 \([l,mid]\) 递归,反之往 \([mid+1,r]\) 递归。
因为最多递归 \(\log\) 次,所以复杂度是 \(O(n \log^2 n)\) 的(\(n,m,k\) 同阶,时间复杂度均以 \(n\) 表示)。
# include <bits/stdc++.h>
# define int long long
const int N=300010,INF=0x3f3f3f3f;
struct Node{
int l,r,val,id;
}a[N<<1];
int opsum;
std::vector <int> idx[N];
int n,m,k;
int sum[N],need[N];
int q1[N],q2[N],q[N],ans[N];
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
inline int lowbit(int x){
return x&(-x);
}
inline void add(int x,int v){
for(;x<=m;x+=lowbit(x))
sum[x]+=v;
return;
}
inline int query(int x){
int ans=0;
for(;x;x-=lowbit(x))
ans+=sum[x];
return ans;
}
inline int getsum(int x){
int ans=0;
for(int i=0;i<(int)idx[x].size();++i){
ans+=query(idx[x][i]);
if(ans>=need[x])
return ans;
}
return ans;
}
void calc(int l,int r,int L,int R){
if(L>R)
return;
if(l==r){
for(int i=L;i<=R;++i)
ans[q[i]]=a[l].id;
return;
}
int mid=(l+r)>>1;
for(int i=l;i<=mid;++i)
add(a[i].l,a[i].val),add(a[i].r+1,-a[i].val);
int pL=0,pR=0;
for(int i=L;i<=R;++i){
int now=getsum(q[i]);
if(now>=need[q[i]])
q1[++pL]=q[i];
else
q2[++pR]=q[i],need[q[i]]-=now;
}
for(int i=1;i<=pL;++i)
q[L+i-1]=q1[i];
for(int i=1;i<=pR;++i)
q[L+pL+i-1]=q2[i];
for(int i=l;i<=mid;++i)
add(a[i].l,-a[i].val),add(a[i].r+1,a[i].val);
calc(l,mid,L,L+pL-1),calc(mid+1,r,L+pL,R);
return;
}
signed main(void){
n=read(),m=read();
for(int i=1;i<=m;++i)
idx[read()].push_back(i);
for(int i=1;i<=n;++i)
need[i]=read(),q[i]=i;
k=read();
for(int i=1;i<=k;++i){
int l=read(),r=read(),val=read();
if(l<=r){
a[++opsum].l=l,a[opsum].r=r,a[opsum].val=val,a[opsum].id=i;
}else{
a[++opsum].l=l,a[opsum].r=m,a[opsum].val=val,a[opsum].id=i;
a[++opsum].l=1,a[opsum].r=r,a[opsum].val=val,a[opsum].id=i; // 环形可以拆成两个
}
}
a[++opsum].l=1,a[opsum].r=m,a[opsum].id=k+1,a[opsum].val=1e9;
calc(1,opsum,1,n);
for(int i=1;i<=n;++i)
if(ans[i]==k+1)
puts("NIE");
else
printf("%lld\n",ans[i]);
return 0;
}