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

博客详细介绍了如何解决CF436F问题,通过将用户按b升序排序,针对每个c枚举广告收入,利用分块策略维护下凸壳,并运用单调队列确保斜率单增,实现优化计算p的最优值,以最大化p*(ai>=p的个数)。整个过程包括分块维护、下凸壳构建、单调队列更新,以及非整块重构,最终达到O(nn√)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先把所有的用户按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 <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
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.b<b.b;}
}A[N];
inline 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]<qt[i]&&slope(q[i][qh[i]],q[i][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]<qt[id]&&slope(q[id][qt[id]],i)<slope(q[id][qt[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;i<bel[x];++i) gao(i,++cnt[i]);
    int 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].b<c) add(A[now].a),++now;
        ll ans=0;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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值