CF436F Banners(分块+单调队列维护下凸壳)

首先把所有的用户按b升序排序,从小到大枚举c,每次广告的收入是可以直接计算出来的,我们只需要确定一个p,使得 p(ai>=p) 最大。不难发现,p一定可以等于某个 ai 时取得最优。因此我们按 ai 分块,每一块维护一个下凸壳,记块内的最优位置。每次求最优时就对所有块的最优位置算一个答案然后取max。

记d[i]表示p=i时的答案,每次往里加一个点x时,对d的影响是:d[1]+=1,…d[x]+=x。对于一整块都加的,我们直接对每一块维护一个cnt,而不加进去,通过下凸壳来找到最优位置。
如果 i<j,d[i]>d[j],d[i]+icnt<=d[j]+jcnt
则有 (d[i]d[j])/(ji)<=cnt 。因此我们要维护斜率单增的单调队列,也就是下凸壳。

对于不是一整块都加的,我们暴力重构这个块的凸包。每个数最多重构一块,所以重构的复杂度是 O(nn) 的。
总的复杂度也就是 O(nn)

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}\
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,w,mxb,mxa,bel[N],nn,cnt[350],q[350][350],qh[350],qt[350];
ll d[N];
struct data{
    int a,b;
    friend bool operator<(data a,data b){return a.binline double slope(int i,int j){return (d[i]-d[j])*1.0/(j-i);}
inline void gao(int i,int x){
    while(qh[i]1])<=x) ++qh[i];
}
inline void rebuild(int id,int l,int r){
    qh[id]=1;qt[id]=0;
    for(int i=l;i<=r;++i){
        while(qh[id]1],q[id][qt[id]])) --qt[id];
        q[id][++qt[id]]=i;
    }gao(id,0);
}
inline void add(int x){
    if(!x) return;
    for(int i=1;iint l=(bel[x]-1)*nn+1,r=min(bel[x]*nn,mxa),id=bel[x];
    for(int i=l;i<=r;++i) d[i]+=(ll)cnt[id]*i;
    for(int i=l;i<=x;++i) d[i]+=i;cnt[id]=0;rebuild(id,l,r);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();w=read();
    for(int i=1;i<=n;++i) A[i].a=read(),A[i].b=read(),mxb=max(mxb,A[i].b),mxa=max(mxa,A[i].a);
    sort(A+1,A+n+1);int now=1;nn=sqrt(mxa);
    for(int i=1;i<=mxa;++i) bel[i]=(i-1)/nn+1;int tot=bel[mxa];
    for(int i=1;i<=tot;++i) qh[i]=1,q[i][++qt[i]]=min(i*nn,mxa);int flag=0;
    for(int c=0;c<=mxb+1;++c){
        while(now<=n&&A[now].b0;int ans1=0;
        for(int i=1;i<=tot;++i){int x=q[i][qh[i]];if(d[x]+(ll)x*cnt[i]>ans) ans=d[x]+(ll)x*cnt[i],ans1=x;}
        printf("%I64d %d\n",ans+(ll)w*c*(n-now+1),ans1);if(c==1) flag=ans+(ll)w*c*(n-now+1);
    }return 0;
}

你可能感兴趣的:(分块,codeforces,单调队列)