Description
话说Nan在海边等人,预计还要等上M分钟。为了打发时间,他玩起了石子。Nan搬来了N堆石子,编号为1到N,每堆
包含Ai颗石子。每1分钟,Nan会在编号在[Li,Ri]之间的石堆中挑出任意Ki颗扔向大海(好疼的玩法),如果[Li,R
i]剩下石子不够Ki颗,则取尽量地多。为了保留扔石子的新鲜感,Nan保证任意两个区间[Li,Ri]和[Lj,Rj],不会
存在Li<=Lj&Rj<=Ri的情况,即任意两段区间不存在包含关系。可是,如果选择不当,可能无法扔出最多的石子,
这时NN就会不高兴了。所以他希望制定一个计划,他告诉你他m分钟打算扔的区间[Li,Ri]以及Ki。现在他想你告诉
他,在满足前i-1分钟都取到你回答的颗数的情况下,第i分钟最多能取多少个石子。
Solution
如果把 \(m\) 个 \(K_i\) 和 \(n\) 堆石子 \(A_i\) 分别拆点 , 那么实际上就是跑一个完美匹配
假设我们钦定了这一分钟所选择的石子个数 , 判断是否合法就是判断是否存在完美匹配了
判断是否存在完美匹配一般用到 \(Hall\) 定理 , 但是问题是需要枚举 \(K_i\) 集合的所有子集
由于这个题目有很好的性质:右端点单调时,左端点也单调
那么有结论: 只需要对于所有的区间 \([l,r]\,\,l,r∈[1,m]\) 满足 \(Hall\) 定理就行了
证明分两种情况,假设有不相邻 \(i,j\) 构成子集:
1.\(i,j\) 所对应的区间不相交,那么 \(i,j\) 分别满足 \(Hall\) 定理就可以了
2.\(i,j\) 有交,那么 \([i+1,j-1]\) 一定都是被 \([L[i],R[j]]\) 包含的 , 所以实际上只需要关心中间连续的一段是否满足 \(Hall\) 定理和整个区间 \([i,j]\) 是否满足 \(Hall\) 定理就可以了
设每个时间选的石子数量为 \(B_i\) ,剩下的就是要时刻满足:
\(\sum_{i=l}^{r}B_i<=\sum_{i=L[l]}^{R[r]}A_i\)
用前缀和表示:
\(SB[r]-SB[l-1]<=SA[R[r]]-SA[L[l]-1]\)
\(SB[R]-SA[R[r]]<=SB[l-1]-SA[L[l]-1]\)
设 \(C[i]=SB[i]-SA[R[i]],D[i]=SB[i-1]-SA[L[i]-1]\)
那么就是对于所有的 \(l<=r\) 有 \(D[l]>=C[r]\)
每一个时刻能取的上界就是 \(min(D[l])-max(C[r])\)
用线段树维护一下即可,时间复杂度 \(O(M*log)\)
#include<bits/stdc++.h>
#define ls (o<<1)
#define rs (o<<1|1)
#define sqr(x) ((x)*(x))
using namespace std;
template<class T>void gi(T &x){
int f;char c;
for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;
}
const int N=4e4+10;
int n,a[N],b[N],id[N],m,p[N],A[N];
struct data{int l,r,id;}q[N];
inline bool operator <(const data &p,const data &q){return p.r<q.r;}
struct seg{
int op,tr[N*4],a[N],la[N*4];
inline int Max(int x,int y){return op?max(x,y):min(x,y);}
inline void build(int l,int r,int o){
if(l==r){tr[o]=a[l];return ;}
int mid=(l+r)>>1;
build(l,mid,ls);build(mid+1,r,rs);
tr[o]=Max(tr[ls],tr[rs]);
}
inline void init(int o){op=o;build(1,m,1);}
inline void pushdown(int o){
if(!la[o])return ;
tr[ls]+=la[o];tr[rs]+=la[o];la[ls]+=la[o];la[rs]+=la[o];la[o]=0;
}
inline int qry(int l,int r,int o,int sa,int se){
if(sa<=l && r<=se)return tr[o];
int mid=(l+r)>>1;pushdown(o);
if(se<=mid)return qry(l,mid,ls,sa,se);
if(sa>mid)return qry(mid+1,r,rs,sa,se);
return Max(qry(l,mid,ls,sa,mid),qry(mid+1,r,rs,mid+1,se));
}
inline void ins(int l,int r,int o,int sa,int se,int t){
if(sa<=l && r<=se){tr[o]+=t;la[o]+=t;return ;}
int mid=(l+r)>>1;pushdown(o);
if(se<=mid)ins(l,mid,ls,sa,se,t);
else if(sa>mid)ins(mid+1,r,rs,sa,se,t);
else ins(l,mid,ls,sa,mid,t),ins(mid+1,r,rs,mid+1,se,t);
tr[o]=Max(tr[ls],tr[rs]);
}
}C,D;
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
int x,y,z,P;
cin>>n>>x>>y>>z>>P;
for(int i=1;i<=n;i++)a[i]=(1ll*sqr(i-x)+sqr(i-y)+sqr(i-z))%P;
cin>>m;
if(!m)return 0;
cin>>b[1]>>b[2]>>x>>y>>z>>P;
for(int i=3;i<=m;i++)b[i]=(x*b[i-1]+y*b[i-2]+z)%P;
for(int i=1;i<=m;i++)gi(q[i].l),gi(q[i].r),q[i].id=i;
sort(q+1,q+m+1);n=0;
for(int i=1,j=1;i<=m;i++){
for(j=max(j,q[i].l);j<=q[i].r;j++)A[id[j]=++n]=a[j];
q[i].l=id[q[i].l];q[i].r=id[q[i].r];p[q[i].id]=i;
}
for(int i=1;i<=n;i++)A[i]+=A[i-1];
for(int i=1;i<=m;i++)C.a[i]=-A[q[i].r],D.a[i]=-A[q[i].l-1];
C.init(1);D.init(0);
for(int i=1;i<=m;i++){
x=p[i];
int rm=C.qry(1,m,1,p[i],m),lm=D.qry(1,m,1,1,p[i]);
int res=min(b[i],lm-rm);
printf("%d\n",res);
C.ins(1,m,1,p[i],m,res);
if(p[i]<m)D.ins(1,m,1,p[i]+1,m,res);
}
return 0;
}