2023NOIP A层联测20 T3 点餐

2023NOIP A层联测20 点餐

题目很好,可惜考试没想到。

思路

可以按照 b 从小到大排序,固定选择个数 k,枚举选择的盘子 xb 最大,最优解肯定是贪心的在前 x1 个盘子里选择 k1 个最小的,使用权值主席树可以在 O(log2n) 的时间内求解。

我们令 f(k) 表示 k 的最优解决策点 x。设 w(k,x)k 的决策点为 x 时的最优答案。对于两个不同的决策 x,y (x<y),若有 w(k,x)>w(k,y) ,那么 k 增大后 x 可以新选的 a 值一定严格包含在 y 可以新选的 a 值以内,即 w(k,x)w(k,y) 对于 kkn 恒成立,所以有 f(k)f(k) (kk)。由此可得 f(1)f(2)f(3)f(n)。决策点具有单调性,可以分治求解(每次选一个区间的中点暴力求,将终点分为两半继续求,具体实现见代码)。

由于分治每一层最多跑 O(n),有 O(log2n) 层,所以要求 O(nlog2n)w(k,i),求一次 w(k,i)O(log2n),时间复杂度为 O(nlog22n)

CODE

#include<bits/stdc++.h>
using namespace std;

#define inf 2e9
#define int long long

const int maxn=2e5+5;

struct node
{
    int ls,rs,sz,sum;
}tree[maxn*50];
struct node1
{
    int a,b;
}food[maxn];

int n,tot;
int ans[maxn],rt[maxn];

bool cmp(node1 a,node1 b){return a.b<b.b;}

void insert(int &p,int x,int l,int r)
{
    tree[++tot]=tree[p];
    p=tot;
    if(l==r)
    {
        tree[p].sum+=x-1;
        tree[p].sz++;
        return ;
    }

    int mid=l+r>>1;
    if(x<=mid) insert(tree[p].ls,x,l,mid);
    else insert(tree[p].rs,x,mid+1,r);

    tree[p].sum=tree[ tree[p].ls ].sum+tree[ tree[p].rs ].sum;
    tree[p].sz=tree[ tree[p].ls ].sz+tree[ tree[p].rs ].sz;
}
int getsum(int p,int l,int r,int k)
{
    if(l==r)  return (l-1)*k;
    int mid=l+r>>1;
    if(tree[tree[p].ls].sz>=k) return getsum(tree[p].ls,l,mid,k);
    else return getsum(tree[p].rs,mid+1,r,k-tree[tree[p].ls].sz)+tree[tree[p].ls].sum;
}

void solve(int l,int r,int lp,int rp)//[l,r] 的个数区间,对于 [lp,rp] 决策区间的点
{
    if(r<l) return ;
    int mid=l+r>>1,pos=0;
    for(int i=max(lp,mid);i<=rp;i++)
    {
        int now=food[i].b+food[i].a+getsum(rt[i-1],1,inf,mid-1);//求 w(mid,i)
        if(ans[mid]>=now)
        {
            ans[mid]=now;
            pos=i;
        }
    }
    solve(l,mid-1,lp,pos);//分析 f 的分布可以得出
    solve(mid+1,r,pos,rp);
}

signed main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld%lld",&food[i].a,&food[i].b);

    sort(food+1,food+n+1,cmp);
    for(int i=1;i<=n;i++)
        insert(rt[i]=rt[i-1],food[i].a+1,1,inf);
    memset(ans,0x5f,sizeof(ans));
    solve(1,n,1,n);

    for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
}
posted @   彬彬冰激凌  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示