BZOJ2138 stone
Link
考虑一个暴力做法:
把每个询问拆成\(k_i\)个点放在左边,每堆石头拆成\(a_i\)个点放在右边,每个询问的点到在这个询问区间内的石头连一条边,这样我们要做的就是判断有没有完美匹配。
根据Hall定理,对于任意一个左边的集合,其相邻的右边的点的个数要大于这个集合的大小。
然后我们发现如果把所有询问按左端点(或者右端点)排个序,那么我们只需要让左边每段区间的点都满足条件就行了。
并且由于每个询问的点连出来的边都是一样的,所以我们可以把它们合到一起判断。右边的每一堆石头同理。
那么也就是说,对于任何一段\([l,r]\)区间内的石头,需要满足询问区间完全包含于\([l,r]\)的询问的\(k\)的和\(\le \sum\limits_{i=l}^r a_i\)。
我们记\(s\)为\(a\)的前缀和数组。
同时我们注意到不存在区间相互包含,所以设\(tl_i\)表示左端点在\([1,i]\)区间内的询问的\(k\)的和,\(tr_i\)表示右端点在\([1,i]\)区间内的询问的\(k\)的和。
那么完全包含于\([l,r]\)的询问的\(k\)的和就是\(tr_r-tl_{l-1}\)。
我们要满足的条件就变成了\(\forall l\le r,tr_r-tl_{l-1}\le s_r-s_{l-1}\)。
根据下标移项得\(s_r-tr_r\ge s_{l-1}-tl_{l-1}\)。
得到这个结论之后我们返回来,再按时间来处理询问。
对于一个询问区间\([l,r]\),我们需要在选完这个区间的石头之后满足\(s_r-tr_r\ge s_{l-1}-tl_{l-1}\)成立。
假如我们在这个区间选了\(x\)个石头,我们发现只有\(tr_r\)会增加\(x\)。
也就是说,\(x\le (s_r-tr_r)-(s_{l-1}-tl_{l-1})\)。
那么当前询问的答案就是\(\min((s_r-tr_r)-(s_{l-1}-tl_{l-1}),k)\)。
\((s_r-tr_r)-(s_{l-1}-tl_{l-1})\)的最小值就是找到\(s_r-tr_r\)的最小值和\(s_{l-1}-tl_{l-1}\)的最大值。
然后更新就是更新\(tl,tr\)的一段后缀。
因此我们需要用两个线段树来分别支持查询区间最小值/最大值和区间加。
#include<bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
#define ll long long
using namespace std;
const int N=40007,inf=1e9;
int read(){int x;scanf("%d",&x);return x;}
int min(int a,int b){return a<b? a:b;}
int max(int a,int b){return a>b? a:b;}
ll sqr(int a){return 1ll*a*a;}
int n,m,a[N],k[N],sum[N],t1[N<<2],t2[N<<2],tag1[N<<2],tag2[N<<2];
void modify1(int p,int v){t1[p]+=v,tag1[p]+=v;}
void modify2(int p,int v){t2[p]+=v,tag2[p]+=v;}
void pushdown1(int p){modify1(ls,tag1[p]),modify1(rs,tag1[p]),tag1[p]=0;}
void pushdown2(int p){modify2(ls,tag2[p]),modify2(rs,tag2[p]),tag2[p]=0;}
void pushup1(int p){t1[p]=min(t1[ls],t1[rs]);}
void pushup2(int p){t2[p]=max(t2[ls],t2[rs]);}
void build1(int p,int l,int r)
{
if(l==r) return (void)(t1[p]=sum[l]);
build1(ls,l,mid),build1(rs,mid+1,r),pushup1(p);
}
void build2(int p,int l,int r)
{
if(l==r) return (void)(t2[p]=sum[l]);
build2(ls,l,mid),build2(rs,mid+1,r),pushup2(p);
}
void update1(int p,int l,int r,int L,int R,int x)
{
if(L<=l&&r<=R) return (void)(tag1[p]+=x,t1[p]+=x);
if(tag1[p]) pushdown1(p);
if(L<=mid) update1(ls,l,mid,L,R,x);
if(R>mid) update1(rs,mid+1,r,L,R,x);
pushup1(p);
}
void update2(int p,int l,int r,int L,int R,int x)
{
if(L<=l&&r<=R) return (void)(tag2[p]+=x,t2[p]+=x);
if(tag2[p]) pushdown2(p);
if(L<=mid) update2(ls,l,mid,L,R,x);
if(R>mid) update2(rs,mid+1,r,L,R,x);
pushup2(p);
}
int query1(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return t1[p];
if(tag1[p]) pushdown1(p);
return min((L<=mid? query1(ls,l,mid,L,R):inf),(R>mid? query1(rs,mid+1,r,L,R):inf));
}
int query2(int p,int l,int r,int L,int R)
{
if(R<L) return 0;
if(L<=l&&r<=R) return t2[p];
if(tag2[p]) pushdown2(p);
return max((L<=mid? query2(ls,l,mid,L,R):0),(R>mid? query2(rs,mid+1,r,L,R):0));
}
int main()
{
n=read();int x,y,z,P;
x=read(),y=read(),z=read(),P=read();
for(int i=1;i<=n;++i) sum[i]=sum[i-1]+(a[i]=(sqr(i-x)+sqr(i-y)+sqr(i-z))%P);
m=read(),k[1]=read(),k[2]=read(),x=read(),y=read(),z=read(),P=read();
for(int i=3;i<=m;++i) k[i]=(x*k[i-1]+y*k[i-2]+z)%P;
build1(1,1,n),build2(1,1,n);
for(int i=1,l,r;i<=m;++i) l=read(),r=read(),printf("%d\n",k[i]=min(k[i],query1(1,1,n,r,n)-query2(1,1,n,1,l-1))),update1(1,1,n,r,n,-k[i]),update2(1,1,n,l,n,-k[i]);
}