[bzoj2138]stone

题目描述

话说Nan在海边等人,预计还要等上M分钟。为了打发时间,他玩起了石子。 Nan搬来了N堆石子,编号为1到N,每堆包含Ai颗石子。每1分钟,Nan会在编号在[Li,Ri]之间的石堆中挑出任意Ki颗扔向大海(好疼的玩法),如果[Li,Ri]剩下石子不够Ki颗,则取尽量地多。为了保留扔石子的新鲜感,Nan保证任意两个区间[Li,Ri]和[Lj,Rj] ,不会存在Li<=Lj & Rj<=Ri的情况,即任意两段区间不存在包含关系。可是,如果选择不当,可能无法扔出最多的石子,这时NN就会不高兴了。所以他希望制定一个计划,他告诉你他m分钟打算扔的区间[Li,Ri]以及Ki。现在他想你告诉他,在满足前i-1分钟都取到你回答的颗数的情况下,第i分钟最多能取多少个石子。

hall定理

我们假设钦定了每次要扔多少颗,怎么判断是否合法呢?
如果网络流呢?
那么就是二分图求最大流是否满流!
把点拆开也可以看做是求有没有完美匹配!
那我们用hall定理吧!
但是hall定理居然是选任意子集QAQ
没事,我们来证明一个结论:
只需要考虑选一个区间。
当然,首先我们要按照右端点排序(每次丢石子对应一段区间),然后我们注意要剔除掉永远不会被拿石子丢的区间。
题目有个性质,保证了右端点有序时左端点也有序,好东西!
假设我们选了一个子集,它们对应的编号是不连续的。
假如是两段,多段的情况是类似的。那么左边那段的最右对应区间如果和右边那段的最左对应区间不相交的话,那么它们是否满足hall定理其实取决于两边是否分别满足hall定理,因此我们并不需要来验证它是否满足hall定理。
那么就考虑一下相交咯!如果相交了的话,把中间那一段补上后,连接的Y集合节点数并没有增多(这是利用了题目的特殊性质),而X集合节点数增多了,所以它是否满足hall定理其实取决于补上后是否满足hall定理,补上后不满足整个图就是不行的,而补上后满足我们也没有验证它的必要了。
因此,我们只需要让每一个区间满足hall定理即可!

推合法情况

设bi表示排序后第i次丢石子丢了多少颗。
ai表示第i个石子堆的石子数。
则对于任意l<=r,都要满足
ri=lbi<=R[r]i=L[l]ai
设s表示b的前缀和,ss表示a前缀和。
同时为了方便,令v[i]=ss[R[i]],vv[i]=ss[L[i+1]-1]
那么上面式子可写为
SrSl1<=VrVVl1
SrVr<=Sl1VVl1
继续为了方便,令c[i]=s[i]-v[i],cc[i]=s[i-1]-vv[i-1]
那么上面式子可写为
Cr<=CCl
只要让这个满足,完美匹配便是存在的。

做法

那么现在按照时间顺序丢石子。
我们每次希望找到最大一个x,把bt赋值为x,使得完美匹配仍存在。
考虑给bt赋值为x的影响。
那么s[t~m]会加上x。
对应有c[t~m]+x,cc[t+1~m]+x。
那么我们考察每一对i<=j是否还满足 Cj<=CCi
如果j在[t,m],i在[t+1,m],因为均加x,关系不变。
如果j不在[t,m],i在[t+1,m],不符合i<=j!
如果j不在[t,m],i不在[t+1,m],因为均加x,关系不变。
只需要看j在[t,m],i不在[t+1,m]的情况!
那么有 Cj+x<=CCi
x<=CCiCj
右式最小值是x的上限值,可以在[1,t]中取CC的最小值,同时在[t,m]中取C的最大值。C和CC的修改和区间最值查询用线段树完成。
然后这题就做完啦~

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=40000+10;
struct dong{
    int time,id;
} b[maxn];
int mx[maxn*4],mi[maxn*4],ad[maxn*4][2];
int s[maxn],ss[maxn],c[maxn],cc[maxn],v[maxn],vv[maxn],a[maxn],L[maxn],R[maxn],K[maxn];
int ans[maxn],g[maxn],bz[maxn],num[maxn];
int i,j,k,l,t,n,m,x,y,z,p,tot,top;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
bool cmp1(dong a,dong b){
    return R[a.time]<R[b.time];
}
bool cmp2(dong a,dong b){
    return a.time<b.time;
}
void mark(int p,int v,int f){
    if (f>0){
        mi[p]+=v;
        ad[p][0]+=v;
    }
    else{
        mx[p]+=v;
        ad[p][1]+=v;
    }
}
void down(int p){
    if (ad[p][0]){
        mark(p*2,ad[p][0],1);
        mark(p*2+1,ad[p][0],1);
        ad[p][0]=0;
    }
    if (ad[p][1]){
        mark(p*2,ad[p][1],-1);
        mark(p*2+1,ad[p][1],-1);
        ad[p][1]=0;
    }
}
void change(int p,int l,int r,int a,int b,int v,int f){
    if (a>b) return;
    if (l==a&&r==b){
        mark(p,v,f);
        return;
    }
    down(p);
    int mid=(l+r)/2;
    if (b<=mid) change(p*2,l,mid,a,b,v,f);
    else if (a>mid) change(p*2+1,mid+1,r,a,b,v,f);
    else{
        change(p*2,l,mid,a,mid,v,f);
        change(p*2+1,mid+1,r,mid+1,b,v,f);
    }
    mx[p]=max(mx[p*2],mx[p*2+1]);
    mi[p]=min(mi[p*2],mi[p*2+1]);
}
int query(int p,int l,int r,int a,int b,int f){
    if (l==a&&r==b) return f>0?mi[p]:mx[p];
    down(p);
    int mid=(l+r)/2;
    if (b<=mid) return query(p*2,l,mid,a,b,f);
    else if (a>mid) return query(p*2+1,mid+1,r,a,b,f);
    else if (f>0) return min(query(p*2,l,mid,a,mid,f),query(p*2+1,mid+1,r,mid+1,b,f));
    else return max(query(p*2,l,mid,a,mid,f),query(p*2+1,mid+1,r,mid+1,b,f));
}
int main(){
    //freopen("data.in","r",stdin);
    n=read();
    x=read();y=read();z=read();p=read();
    fo(i,1,n) a[i]=((i-x)*(i-x)%p+(i-y)*(i-y)%p+(i-z)*(i-z)%p)%p;
    m=read();
    K[1]=read();K[2]=read();x=read();y=read();z=read();p=read();
    fo(i,3,m) K[i]=(x*K[i-1]+y*K[i-2]+z)%p;
    fo(i,1,m){
        L[i]=read(),R[i]=read();
        g[L[i]]=max(g[L[i]],R[i]-L[i]+1);
    }
    k=0;
    fo(i,1,n){
        if (k) k--;
        k=max(k,g[i]);
        if (!k) bz[i]=1;
        num[i]=num[i-1]+bz[i];
    }
    fo(i,1,n) 
        if (!bz[i]) a[i-num[i]]=a[i];
    fo(i,1,m) L[i]-=num[L[i]],R[i]-=num[R[i]];
    n-=num[n];
    fo(i,1,n) ss[i]=ss[i-1]+a[i];
    fo(i,1,m) b[i].time=i;
    sort(b+1,b+m+1,cmp1);
    fo(i,1,m) b[i].id=i;
    fo(i,1,m){
        v[i]=ss[R[b[i].time]];
        if (i<m) vv[i]=ss[L[b[i+1].time]-1];
        c[i]=s[i]-v[i];
        cc[i]=s[i-1]-vv[i-1];
    }
    fo(i,1,m) change(1,1,m,i,i,c[i],-1);
    fo(i,1,m) change(1,1,m,i,i,cc[i],1);
    sort(b+1,b+m+1,cmp2);
    fo(i,1,m){
        t=b[i].id;
        j=query(1,1,m,t,m,-1);
        k=query(1,1,m,1,t,1);
        ans[i]=min(K[i],k-j);
        change(1,1,m,t,m,ans[i],-1);
        change(1,1,m,t+1,m,ans[i],1);
    }
    fo(i,1,m) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值