[BZOJ2138]stone Hall定理+線段樹
阿新 • • 發佈:2019-02-01
假設把每堆石子拆成個點,每個詢問拆成個點,就相當於每次新增個點,然後詢問此時的最大匹配能增加多少。
通過Hall定理可以判斷匹配的合法性。但因為本題的區間沒有包含,把詢問按照排序,是遞增的,在剔除掉沒有被任一區間覆蓋的石子堆之後,一段詢問區間對應的石子也是一段連續的區間,我們不需要判斷每個子集,而只需要判斷每個區間是否滿足Hall定理即可。
設是排序後第個詢問所選的石子個數,那麼只要滿足對於任意,有
即可。
證明的話考慮要判斷兩個中間不相連的詢問區間是否滿足Hall定理(隨之可以推廣到多個即子集),假設這兩個詢問區間對應的石子區間也是不相連的兩段,那麼整體是否滿足就取決於這兩對區間是否分別滿足。如果對應的石子區間相連,考慮把兩個詢問區間中間的點補上,而對應的石子區間並沒有增加,這對區間滿足Hall定理的條件顯然比原問題的條件緊,於是得證。
我們考慮把式子化成字首和的形式:
於是我們按時間順序新增詢問,假如當前是給找最大值,那麼顯然只有才會限制,於是我們找到的的最大值,的的最小值就能得出的最大取值,然後把之後的加上即可。
上述操作都用線段樹維護。
程式碼:
#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;
}