【BZOJ2138】stone Hall定理+线段树

【BZOJ2138】stone

Description

话说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分钟最多能取多少个石子。

Input

第一行正整数N,表示石子的堆数;
第二行正整数x,y,z,P,(1<=x,y,z<=N;P<=500) 
有等式A[i]=[(i-x)^2+(i-y)^2+(i-z)^2] mod P;
第三行正整数M,表示有M分钟;
第四行正整数K[1],K[2],x,y,z,P,(x,y,z<=1000;P<=10000) 
有等式K[i]=(x*K[i-1]+y*K[i-2]+z)mod P。
接下来M行,每行两个正整数L[i],R[i]。
N<=40000   M<=N   1<=L[i]<=R[i]<=N   A[i]<=500

Output

有M行,第i行表示第i分钟最多能取多少石子。

Sample Input

5
3 2 4 7
3
2 5 2 6 4 9
2 4
1 2
3 5

Sample Output

2
5
5
【样例说明】
石子每堆个数分别为0,5,2,5,0。
第1分钟,从第2到第4堆中选2个;
第2分钟,从第1到第2堆中选5个;
第3分钟,从第3到第5堆中选8个,但最多只能选5个。

题解:如果把每堆石子拆成$A_i$个,将询问拆成$K_i$个,则原题可看成一个二分图最大匹配的模型。这里要应用到Hall定理。

Hall定理:两个集合$X$和$Y$进行匹配,最大匹配为$|X|$的充要条件是:对于X的任意一个子集S,设Y中与S相邻的点集为T,满足$|S|\le |T|$。

但是定理里面写的是任意一个子集,而我们想把它转化成区间上的形式,不难想到:在本题中,我们是否可以不枚举所有石子的所有子集,而是只枚举所有的区间是否满足条件呢?答案是肯定的,证明也非常简单:

首先,如果我们取了一个询问,但是没有全取,显然这种情况是不需要讨论的,因为同一个询问中每个石子的邻集都是相同的,如果全取完满足条件的话不全取完也一定满足条件。

其次,如果我们取的询问不是连续的一段区间,这种情况也是没有意义的。因为题中满足询问的r随着l增大而增大,如果我们选的两个的询问段不相交,则我们完全可以分开考虑,看每一段是否满足条件即可。

然后就轻松多啦!我们将询问按位置排序,并剔除掉所有没有用到的堆,设第i个询问我们取了Bi个,那么对于任意$1\le l\le r\le m$,需要满足:

$\sum\limits_{i=l}^r B[i]\le \sum\limits_{i=L[l]}^{R[r]}A[i]$

改成前缀和的形式就是:

$sb[r]-sb[l-1]\le sa[R[r]]-sa[L[l]-1]$
$sb[r]-sa[R[r]]\le sb[l-1]-sa[L[l]-1]$

我们设$C[i]=sb[i]-sa[R[i]],D[i]=sb[i-1]-sa[L[i]-1]$,则限制就变成了
$C[r]\le D[l]$

所以,我们只需要按时间序处理询问,然后在线段树上统计右面C的最大值以及左边D的最小值就能得到当前B的最大值了。然后将i...n的C都+B,i+1...n的D都+B即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=40010;
int X,Y,Z,P;
int n,m;
int L[maxn],R[maxn],A[maxn],B[maxn],ref[maxn],sa[maxn],sb[maxn],p[maxn],C[maxn],D[maxn];
struct node
{
	int l,r,org;
}q[maxn];
struct sag
{
	int s[maxn<<2],tag[maxn<<2],flag;
	inline int MX(int a,int b)
	{
		if(flag==0)	return max(a,b);
		else	return min(a,b);
	}
	inline void pushdown(int x)
	{
		if(tag[x])	s[lson]+=tag[x],s[rson]+=tag[x],tag[lson]+=tag[x],tag[rson]+=tag[x],tag[x]=0;
	}
	void build(int l,int r,int x)
	{
		if(l==r)
		{
			if(flag==0)	s[x]=C[l];
			else	s[x]=D[l];
			return ;
		}
		int mid=(l+r)>>1;
		build(l,mid,lson),build(mid+1,r,rson);
		s[x]=MX(s[lson],s[rson]);
	}
	void updata(int l,int r,int x,int a,int b,int c)
	{
		if(a<=l&&r<=b)
		{
			s[x]+=c,tag[x]+=c;
			return ;
		}
		pushdown(x);
		int mid=(l+r)>>1;
		if(a<=mid)	updata(l,mid,lson,a,b,c);
		if(b>mid)	updata(mid+1,r,rson,a,b,c);
		s[x]=MX(s[lson],s[rson]);
	}
	int query(int l,int r,int x,int a,int b)
	{
		if(a<=l&&r<=b)	return s[x];
		pushdown(x);
		int mid=(l+r)>>1;
		if(b<=mid)	return query(l,mid,lson,a,b);
		if(a>mid)	return query(mid+1,r,rson,a,b);
		return MX(query(l,mid,lson,a,b),query(mid+1,r,rson,a,b));
	}
}SC,SD;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
	return ret*f;
}
bool cmp(const node &a,const node &b)
{
	return a.l<b.l;
}
int main()
{
	n=rd();
	int i,j;
	X=rd(),Y=rd(),Z=rd(),P=rd();
	for(i=1;i<=n;i++)	A[i]=((i-X)*(i-X)+(i-Y)*(i-Y)+(i-Z)*(i-Z))%P;
	m=rd();
	if(!m)	return 0;
	B[1]=rd(),B[2]=rd(),X=rd(),Y=rd(),Z=rd(),P=rd();
	for(i=3;i<=m;i++)	B[i]=(X*B[i-1]+Y*B[i-2]+Z)%P;
	for(i=1;i<=m;i++)	q[i].l=rd(),q[i].r=rd(),q[i].org=i;
	sort(q+1,q+m+1,cmp);
	for(i=1,j=n=0;i<=m;i++)
	{
		for(j=max(j,q[i].l);j<=q[i].r;j++)	A[++n]=A[j],ref[j]=n;
		L[q[i].org]=ref[q[i].l],R[q[i].org]=ref[q[i].r],p[q[i].org]=i;
	}
	for(i=1;i<=n;i++)	sa[i]=sa[i-1]+A[i];
	for(i=1;i<=m;i++)	C[p[i]]=-sa[R[i]],D[p[i]]=-sa[L[i]-1];
	SC.flag=0,SD.flag=1,SC.build(1,m,1),SD.build(1,m,1);
	for(i=1;i<=m;i++)
	{
		int rm=SC.query(1,m,1,p[i],m),ln=SD.query(1,m,1,1,p[i]);
		B[i]=min(B[i],ln-rm);
		printf("%d\n",B[i]);
		SC.updata(1,m,1,p[i],m,B[i]);
		if(p[i]!=m)	SD.updata(1,m,1,p[i]+1,m,B[i]);
	}
	return 0;
}
posted @ 2018-02-25 11:58  CQzhangyu  阅读(834)  评论(0编辑  收藏  举报