假设把每堆石子拆成
Ai
A
i
个点,每个询问拆成
Ki
K
i
个点,就相当于每次添加
Ki
K
i
个点,然后询问此时的最大匹配能增加多少。
通过Hall定理可以判断匹配的合法性。但因为本题的区间没有包含,把询问按照
Li
L
i
排序,
Ri
R
i
是递增的,在剔除掉没有被任一区间覆盖的石子堆之后,一段询问区间对应的石子也是一段连续的区间,我们不需要判断每个子集,而只需要判断每个区间是否满足Hall定理即可。
设
Bi
B
i
是排序后第
i
i
个询问所选的石子个数,那么只要满足对于任意,有
即可。
证明的话考虑要判断两个中间不相连的询问区间是否满足Hall定理(随之可以推广到多个即子集),假设这两个询问区间对应的石子区间也是不相连的两段,那么整体是否满足就取决于这两对区间是否分别满足。如果对应的石子区间相连,考虑把两个询问区间中间的点补上,而对应的石子区间并没有增加,这对区间满足Hall定理的条件显然比原问题的条件紧,于是得证。
我们考虑把式子化成前缀和的形式: sbr−sbl−1≤saRr−saLl−1 s b r − s b l − 1 ≤ s a R r − s a L l − 1 。设 Ci=sbi−Ri,Di=sai−1−saLi−1 C i = s b i − R i , D i = s a i − 1 − s a L i − 1 ,那么就是 Cr≤Dl C r ≤ D l 。
于是我们按时间顺序添加询问,假如当前是给 Bi B i 找最大值,那么显然只有 l<i,r>i l < i , r > i 才会限制,于是我们找到 r>i r > i 的 Ci C i 的最大值, l<i l < i 的 Di D i 的最小值就能得出 Bi B i 的最大取值,然后把 i i 之后的加上 Bi B i 即可。
上述操作都用线段树维护。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 40010
#define ll long long
#define mid (l+r>>1)
using namespace std;
int n,m,a[N],o[N];
struct node
{
int l,r,k,t,id;
}b[N];
bool cmpl(node p,node q)
{
return p.l<q.l;
}
bool cmpt(node p,node q)
{
return p.t<q.t;
}
int read()
{
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
struct tree
{
int s[N<<2],add[N<<2];
bool flag;
int M(int x,int y)
{
return flag?max(x,y):min(x,y);
}
void update(int v)
{
s[v]=M(s[v<<1],s[v<<1|1]);
}
void cal(int v,int d)
{
s[v]+=d;add[v]+=d;
}
void pushdown(int v)
{
if(add[v])
{
cal(v<<1,add[v]);
cal(v<<1|1,add[v]);
add[v]=0;
}
}
void build(int v,int l,int r)
{
if(l==r) {s[v]=flag?-a[b[r].r]:-a[b[l].l-1];return ;}
build(v<<1,l,mid);
build(v<<1|1,mid+1,r);
update(v);
}
void mdf(int v,int l,int r,int lx,int rx,int c)
{
if(l==lx&&r==rx) {cal(v,c);return ;}
pushdown(v);
if(rx<=mid) mdf(v<<1,l,mid,lx,rx,c);
else if(lx>mid) mdf(v<<1|1,mid+1,r,lx,rx,c);
else mdf(v<<1,l,mid,lx,mid,c),mdf(v<<1|1,mid+1,r,mid+1,rx,c);
update(v);
}
int qry(int v,int l,int r,int lx,int rx)
{
if(l==lx&&r==rx) return s[v];
pushdown(v);
if(rx<=mid) return qry(v<<1,l,mid,lx,rx);
if(lx>mid) return qry(v<<1|1,mid+1,r,lx,rx);
return M(qry(v<<1,l,mid,lx,mid),qry(v<<1|1,mid+1,r,mid+1,rx));
}
}Tl,Tr;
int main()
{
n=read();
int x=read(),y=read(),z=read(),p=read();
for(int i=1;i<=n;i++)
a[i]=((ll)(i-x)*(i-x)+(i-y)*(i-y)+(i-z)*(i-z))%p;
m=read();
if(!m) return 0;
b[1].k=read();b[2].k=read();x=read();y=read();z=read();p=read();
for(int i=3;i<=m;i++)
b[i].k=((ll)x*b[i-1].k+y*b[i-2].k+z)%p;
for(int i=1;i<=m;i++)
b[i].l=read(),b[i].r=read(),b[i].t=i;
sort(b+1,b+m+1,cmpl);
for(int i=1;i<=m;i++)
b[i].id=i;
b[m+1].l=n+1;
for(int i=1;i<=m+1;i++)
{
int tmp=b[i].l-b[i-1].r-1;
if(tmp>0) o[b[i].l]+=tmp;
}
for(int i=1;i<=n;i++)
o[i]+=o[i-1];
for(int i=1;i<=n;i++)
a[i-o[i]]=a[i];
for(int i=1;i<=m;i++)
b[i].l-=o[b[i].l],b[i].r-=o[b[i].r];
n-=o[n];
for(int i=1;i<=n;i++)
a[i]+=a[i-1];
Tl.flag=0;Tr.flag=1;
Tl.build(1,1,m);
Tr.build(1,1,m);
sort(b+1,b+m+1,cmpt);
for(int i=1;i<=m;i++)
{
int ans=min(Tl.qry(1,1,m,1,b[i].id)-Tr.qry(1,1,m,b[i].id,m),b[i].k);
printf("%d\n",ans);
if(b[i].id<m) Tl.mdf(1,1,m,b[i].id+1,m,ans);
Tr.mdf(1,1,m,b[i].id,m,ans);
}
return 0;
}