2023NOIP A层联测20 点餐
题目很好,可惜考试没想到。
思路
可以按照 \(b\) 从小到大排序,固定选择个数 \(k\),枚举选择的盘子 \(x\) 的 \(b\) 最大,最优解肯定是贪心的在前 \(x-1\) 个盘子里选择 \(k-1\) 个最小的,使用权值主席树可以在 \(O(\log_2n)\) 的时间内求解。
我们令 \(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)\geq w(k',y)\) 对于 \(k\leq k'\leq n\) 恒成立,所以有 \(f(k)\leq f(k')\ (k\leq k')\)。由此可得 \(f(1)\leq f(2) \leq f(3) \leq \cdots \leq f(n)\)。决策点具有单调性,可以分治求解(每次选一个区间的中点暴力求,将终点分为两半继续求,具体实现见代码)。
由于分治每一层最多跑 \(O(n)\),有 \(O(\log_2 n)\) 层,所以要求 \(O(n\log_2 n)\) 次 \(w(k,i)\),求一次 \(w(k,i)\) 要 \(O(\log_2 n)\),时间复杂度为 \(O(n\log_2^2 n)\)。
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]);
}
标签:20,log,int,tree,mid,leq,ls,点餐,2023NOIP
From: https://www.cnblogs.com/binbinbjl/p/17799095.html