1. 程式人生 > >[BZOJ2138]stone Hall定理+線段樹

[BZOJ2138]stone Hall定理+線段樹

假設把每堆石子拆成Ai個點,每個詢問拆成Ki個點,就相當於每次新增Ki個點,然後詢問此時的最大匹配能增加多少。
通過Hall定理可以判斷匹配的合法性。但因為本題的區間沒有包含,把詢問按照Li排序,Ri是遞增的,在剔除掉沒有被任一區間覆蓋的石子堆之後,一段詢問區間對應的石子也是一段連續的區間,我們不需要判斷每個子集,而只需要判斷每個區間是否滿足Hall定理即可。
Bi是排序後第i個詢問所選的石子個數,那麼只要滿足對於任意lr,有

i=lrBii=LlRrAi
即可。
證明的話考慮要判斷兩個中間不相連的詢問區間是否滿足Hall定理(隨之可以推廣到多個即子集),假設這兩個詢問區間對應的石子區間也是不相連的兩段,那麼整體是否滿足就取決於這兩對區間是否分別滿足。如果對應的石子區間相連,考慮把兩個詢問區間中間的點補上,而對應的石子區間並沒有增加,這對區間滿足Hall定理的條件顯然比原問題的條件緊,於是得證。
我們考慮把式子化成字首和的形式:
sbrsbl1saRrsaLl1
。設Ci=sbiRi,Di=sai1saLi1,那麼就是CrDl
於是我們按時間順序新增詢問,假如當前是給Bi找最大值,那麼顯然只有l<i,r>i才會限制,於是我們找到r>iCi的最大值,l<iDi的最小值就能得出Bi的最大取值,然後把i之後的C,D加上Bi即可。
上述操作都用線段樹維護。

程式碼:

#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; }