给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值
https://www.luogu.org/problemnew/show/P3527
每一次计算出mid时刻每个国家已经收集的数量,把所有能够完成国家的放在左边,所有不能完成的国家放在右边。左边继续二分mid以下的时间,右边继续二分mid以上的时间。
判断能否完成,用树状数组暴力更新即可。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long LL;
LL c[300005];
vector<int> G[300005];
int L[300005],R[300005],val[300005];
int P[300005],ans[300005];
int q[300005],q1[300005],q2[300005];
int n,m,k,now=0;
inline int read() {
int sum=0;char ch=0;
while (ch>'9' || ch<'0') ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
return sum;
}
inline int lowbit(int x) {
return x&-x;
}
inline void add(int x,int v) {
for (;x<=m;x+=lowbit(x)) c[x]+=v;
}
inline LL query(int x) {
LL tmp=0;
for (;x>0;x-=lowbit(x)) tmp+=c[x];
return tmp;
}
inline void update(int l,int r,int v) {
if (l<=r) {
add(l,v);add(r+1,-v);
}
else {
add(1,v);add(r+1,-v);
add(l,v);add(m+1,-v);
}
}
void bs(int l,int r,int s,int t) {//lr二分k st代表国家位置
if (s>t) return;
if (l==r) {
for (int i=s;i<=t;i++) ans[q[i]]=l;
return;
}
int mid=(l+r)>>1;
while (now<mid) now++,update(L[now],R[now],val[now]);
while (now>mid) update(L[now],R[now],-val[now]),now--;
int s1=0,s2=0;
for (int i=s;i<=t;i++) {
LL tmp=0;
int x=q[i];
for (int j=0;j<G[x].size();j++) {
tmp+=query(G[x][j]);
if (tmp>=P[x]) break;
}
if (tmp>=P[x]) q1[++s1]=x;
else q2[++s2]=x;
}
for (int i=1;i<=s1;i++) q[s+i-1]=q1[i];
for (int i=1;i<=s2;i++) q[s+s1+i-1]=q2[i];
bs(l,mid,s,s+s1-1);
bs(mid+1,r,s+s1,t);
}
int main() {
n=read();m=read();
for (int i=1;i<=m;i++) G[read()].push_back(i);
for (int i=1;i<=n;i++) {
P[i]=read();
q[i]=i;
}
k=read();
for (int i=1;i<=k;i++) {
L[i]=read();R[i]=read();val[i]=read();
}
bs(1,k+1,1,n);
for (int i=1;i<=n;i++) {
if (ans[i]==k+1) printf("NIE\n");
else printf("%d\n",ans[i]);
}
return 0;
}