【BZOJ2138】stone Hall定理+线段树
【BZOJ2138】stone
Description
Input
Output
有M行,第i行表示第i分钟最多能取多少石子。
Sample Input
3 2 4 7
3
2 5 2 6 4 9
2 4
1 2
3 5
Sample Output
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; }
| 欢迎来原网站坐坐! >原文链接<