Description
给定长度为n的序列A,ai表示位置i有多少个石子。
有m次操作,第i次操作从区间[li,ri]中选择ki个石子丢弃,如果不足ki个则全部丢弃
保证区间互不相交
最大化丢弃石子的字典序
n,m<=40000
Solution
考虑如果我们已经知道了每次要拿几个,如何判断是否合法?
一个显然的思路是网络流判断是否满流,换句话说是拆点判断完美匹配
hall定理!但是hall定理要枚举子集怎么办?
有一个结论是,如果每个X部的点向Y部连的都是一个区间且区间没有交集
只需要对按区间右端点排序之后的X部的连续区间验证hall定理即可
这个很显然可以反证
我们将a中没有被覆盖的点删去,设A为a的前缀和
设bi表示排序后第i次操作拿的石头数,B为b的前缀和
那么我们相当于要求
∀
i
<
j
,
B
j
−
B
i
−
1
<
=
A
r
j
−
A
l
i
−
1
\forall i<j,B_j-B_{i-1}<=A_{rj}-A_{li-1}
∀i<j,Bj−Bi−1<=Arj−Ali−1
设
C
j
=
B
j
−
A
r
j
,
D
i
=
B
i
−
1
−
A
l
i
−
1
C_j=B_j-A_{rj},D_i=B_{i-1}-A_{li-1}
Cj=Bj−Arj,Di=Bi−1−Ali−1,我们要求
∀
i
<
j
,
C
j
<
=
D
i
\forall i<j,C_j<=D_i
∀i<j,Cj<=Di
考虑按时间处理操作,假如我们要让bt+=x
那么对于i∈[t+1,q],j∈[t,q],Cj和Di都会+=x
考虑对限制的影响,显然是有j∈[t,q]和i∈[1,t]会有影响
也就是
C
j
+
x
<
=
D
i
C_j+x<=D_i
Cj+x<=Di
x
<
=
D
i
−
C
j
x<=D_i-C_j
x<=Di−Cj
显然D取最小值C取最大值就是x的上界
这个东西随便用一个线段树维护一下就好了
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int read() {
char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
int x=ch-'0';
for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
const int N=4e4+5,inf=0x7fffffff;
int n,m,a[N],c[N],cnt[N],id[N],rk[N],mx[N<<2],mn[N<<2],tmx[N<<2],tmn[N<<2];
int sqr(int x) {return x*x;}
void add(int x,int z,int opt) {
if (opt==0) mx[x]+=z,tmx[x]+=z;
if (opt==1) mn[x]+=z,tmn[x]+=z;
}
void down(int v) {
if (tmx[v]) {
add(v<<1,tmx[v],0);
add(v<<1|1,tmx[v],0);
tmx[v]=0;
}
if (tmn[v]) {
add(v<<1,tmn[v],1);
add(v<<1|1,tmn[v],1);
tmn[v]=0;
}
}
void update(int v) {
mx[v]=max(mx[v<<1],mx[v<<1|1]);
mn[v]=min(mn[v<<1],mn[v<<1|1]);
}
void modify(int v,int l,int r,int x,int y,int z,int opt) {
if (x>y) return;
if (x<=l&&r<=y) {add(v,z,opt);return;}
int mid=l+r>>1;down(v);
if (x<=mid) modify(v<<1,l,mid,x,y,z,opt);
if (y>mid) modify(v<<1|1,mid+1,r,x,y,z,opt);
update(v);
}
int query_min(int v,int l,int r,int x,int y) {
if (x<=l&&r<=y) return mn[v];
int mid=l+r>>1,tmp=inf;down(v);
if (x<=mid) tmp=min(tmp,query_min(v<<1,l,mid,x,y));
if (y>mid) tmp=min(tmp,query_min(v<<1|1,mid+1,r,x,y));
return tmp;
}
int query_max(int v,int l,int r,int x,int y) {
if (x<=l&&r<=y) return mx[v];
int mid=l+r>>1,tmp=-inf;down(v);
if (x<=mid) tmp=max(tmp,query_max(v<<1,l,mid,x,y));
if (y>mid) tmp=max(tmp,query_max(v<<1|1,mid+1,r,x,y));
return tmp;
}
struct Q{int l,r,k,id;}q[N];
bool cmp1(Q a,Q b) {return a.r<b.r;}
bool cmp2(Q a,Q b) {return a.id<b.id;}
void init() {
n=read();
int x=read(),y=read(),z=read(),p=read();
fo(i,1,n) a[i]=(sqr(i-x)%p+sqr(i-y)%p+sqr(i-z)%p)%p;
m=read();
q[1].k=read();q[2].k=read();x=read(),y=read(),z=read(),p=read();
fo(i,3,m) q[i].k=(x*q[i-1].k%p+y*q[i-2].k%p+z)%p;
fo(i,1,m) q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+m+1,cmp1);
fo(i,1,m) rk[q[i].id]=i;
sort(q+1,q+m+1,cmp2);
}
void prepare() {
fo(i,1,m) {
cnt[q[i].l]++;
cnt[q[i].r+1]--;
}
fo(i,1,n) cnt[i]+=cnt[i-1];
int Id=0;
fo(i,1,n) if (cnt[i]) id[i]=++Id,c[Id]=a[i];
fo(i,1,m) q[i].l=id[q[i].l],q[i].r=id[q[i].r];
n=Id;fo(i,1,n) a[i]=c[i];
fo(i,1,n) a[i]+=a[i-1];
}
int main() {
freopen("forget.in","r",stdin);
freopen("forget.out","w",stdout);
init();prepare();
fo(i,1,m) {
modify(1,1,m,rk[i],rk[i],-a[q[i].r],0);
modify(1,1,m,rk[i],rk[i],-a[q[i].l-1],1);
}
fo(i,1,m) {
int t=rk[i];
int x=query_min(1,1,m,1,t)-query_max(1,1,m,t,m);
x=min(x,q[i].k);
printf("%d\n",x);
modify(1,1,m,rk[i],m,x,0);
modify(1,1,m,rk[i]+1,m,x,1);
}
return 0;
}