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;
}

posted on 2020-04-10 11:45  autoint  阅读(116)  评论(0编辑  收藏  举报

导航