BZOJ2138 stone
stone
话说Nan在海边等人,预计还要等上M分钟。为了打发时间,他玩起了石子。Nan搬来了N堆石子,编号为1到N,每堆包含Ai颗石子。每1分钟,Nan会在编号在[Li,Ri]之间的石堆中挑出任意Ki颗扔向大海(好疼的玩法),如果[Li,Ri]剩下石子不够Ki颗,则取尽量地多。
为了保留扔石子的新鲜感,Nan保证任意两个区间[Li,Ri]和[Lj,Rj],不会存在Li<=Lj&Rj<=Ri的情况,即任意两段区间不存在包含关系。
可是,如果选择不当,可能无法扔出最多的石子,这时NN就会不高兴了。所以他希望制定一个计划,他告诉你他m分钟打算扔的区间[Li,Ri]以及Ki。现在他想你告诉
他,在满足前i-1分钟都取到你回答的颗数的情况下,第i分钟最多能取多少个石子。
N<=40000
题解
https://www.cnblogs.com/cjyyb/p/9301820.html
考虑一个暴力。每堆石子和每个询问,显然是匹配的操作。
所以可以把石子拆成ai个,询问点拆成Ki个,这样就是每次进行一次二分图的匹配。当然可以用网络流+线段树优化连边来做,但是这样复杂度太高。
还是回到二分图的匹配问题,我们现在要验证的就是是否存在对于当前询问点的完美匹配。
关于完美匹配,有Hall定理,如果存在完美匹配,假设左侧的点有|X|个,那么这些点连向右边的点的点集的并Y,满足|X|≤|Y|
因为询问点拆开后,每个点的连向右边的点集都是一样的,所以相当于就是Ki≤|Y|
只提取出所有有用的石子,按顺序编号。设si表示前i堆石子的个数和。
如果存在完美匹配,那么在任意时刻,所有存在于区间[L,R]之内的询问的石子个数的总和T[L,R]一定小于区间[L,R]之内的石子的总和。
也就是sR−sL−1≥T[L,R],发现题目中的性质,任何询问不存在包含关系。
那么我们假设TLi表示左端点在[1,i]中的询问的总和,TRi表示右端点在[1,i]中的询问的总和。
那么因为T[L,R]=TRR−TLL−1,所以我们也可以很容易的表示出T来。
所以,现在的不等式表达为s[R]−s[L−1]≥TR[R]−TL[L−1]
所以s[R]−TR[R]≥s[L−1]−TL[L−1]
设f[i]=s[i]−TR[i],g[i]=s[i]−TL[i],所以是f[R]≥g[L−1]
我们发现,如果从[L,R]区间中拿走若干石头,在不等式中变化的只有TR[R]
也就是只有f[i]会减小。所以我们能够拿走的数量为min(K[i],f[R]−g[L−1])
对于当前询问区间[L,R],会对于所有的x∈[1,L],y∈[R,n],[x,y]产生影响
也就是任何包含当前区间的区间也需要满足Hall定理,在本题中也就是f[y]≥g[x]
那么当前步的答案就是所有的min(K[i],f[y]−g[x]),那么取后缀f最小值,前缀g最大值即可。
每次拿线段树区间更新一下即可。
CO int N=3e5+10;
int sum[N];
namespace mn{
int val[4*N],tag[4*N];
#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
IN void push_up(int x){
val[x]=min(val[lc],val[rc]);
}
IN void push_down(int x){
if(tag[x]){
val[lc]+=tag[x],tag[lc]+=tag[x];
val[rc]+=tag[x],tag[rc]+=tag[x];
tag[x]=0;
}
}
void build(int x,int l,int r){
if(l==r) {val[x]=sum[l]; return;}
build(lc,l,mid),build(rc,mid+1,r);
push_up(x);
}
void modify(int x,int l,int r,int ql,int qr,int v){
if(ql<=l and r<=qr) {val[x]+=v,tag[x]+=v; return;}
push_down(x);
if(ql<=mid) modify(lc,l,mid,ql,qr,v);
if(qr>mid) modify(rc,mid+1,r,ql,qr,v);
push_up(x);
}
int query(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return val[x];
push_down(x);
if(qr<=mid) return query(lc,l,mid,ql,qr);
if(ql>mid) return query(rc,mid+1,r,ql,qr);
return min(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
}
#undef lc
#undef rc
#undef mid
}
namespace mx{
int val[4*N],tag[4*N];
#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
IN void push_up(int x){
val[x]=max(val[lc],val[rc]);
}
IN void push_down(int x){
if(tag[x]){
val[lc]+=tag[x],tag[lc]+=tag[x];
val[rc]+=tag[x],tag[rc]+=tag[x];
tag[x]=0;
}
}
void build(int x,int l,int r){
if(l==r) {val[x]=sum[l]; return;}
build(lc,l,mid),build(rc,mid+1,r);
push_up(x);
}
void modify(int x,int l,int r,int ql,int qr,int v){
if(ql<=l and r<=qr) {val[x]+=v,tag[x]+=v; return;}
push_down(x);
if(ql<=mid) modify(lc,l,mid,ql,qr,v);
if(qr>mid) modify(rc,mid+1,r,ql,qr,v);
push_up(x);
}
int query(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return val[x];
push_down(x);
if(qr<=mid) return query(lc,l,mid,ql,qr);
if(ql>mid) return query(rc,mid+1,r,ql,qr);
return max(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
}
#undef lc
#undef rc
#undef mid
}
int main(){
freopen("flandre.in","r",stdin),freopen("flandre.out","w",stdout);
int n=read<int>();
for(int i=1;i<=n;++i) sum[i]=sum[i-1]+read<int>();
mn::build(1,1,n),mx::build(1,0,n);
for(int m=read<int>();m--;){
int l=read<int>(),r=read<int>(),k=read<int>();
k=min(k,mn::query(1,1,n,r,n)-mx::query(1,0,n,0,l-1));
mn::modify(1,1,n,r,n,-k),mx::modify(1,0,n,l,n,-k);
printf("%d\n",k);
}
return 0;
}