题解:P11523 [THUPC2025 初赛] 摊位分配
· 阅读需 3 分钟
原题链接
解题思路
序列 是单调递减的,显然只有取到 后,才能取到 。因此,可以维护一个大顶堆,初始时将每个社团的权值都放入堆中。
枚举每个格子,每次从大顶堆中取出权值最大的社团,将该格子分配给该社团,并将其权值减半后重新放回堆中。这种方法的时间复杂度为 ,还可能因浮点运算产生精度问题,无法通过此题。
考虑优化,注意到任意权值 都可以表示为 ,其中 且 。当大顶堆中堆顶元素小于 时,堆内所有元素均位于区间 。
此时有个很好的性质,即对于任意 ,满足:
设还剩 个待分配的格子,每个社团会先被分配 个格子,剩下的 个格子将优先分配给权值 最大的 个社团。
显然,对于每个 ,其 ,因此时间复杂度 。
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=100005;
struct Node
{
int id,cnt;
double val,st;
bool operator<(const Node &rhs) const
{
if(val!=rhs.val)return val<rhs.val;
if(cnt==0^rhs.cnt==0)return cnt;
if(st!=rhs.st)return st<rhs.st;
return id>rhs.id;
}
}a[N];
int ans[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,k;
cin>>n>>k;
priority_queue<Node> q;
for(int i=1;i<=n;i++)
{
double x;
cin>>x;
q.push({i,0,x,x});
}
while(!q.empty()&&k)
{
Node u=q.top();
if(u.val<2)break;
q.pop();
u.cnt++;
u.val/=2;
q.push(u);
k--;
}
while(!q.empty())
{
Node u=q.top();
q.pop();
a[u.id]=u;
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
ans[a[i].id]=a[i].cnt+k/n+(i>n-k%n);
}
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<' ';
}
return 0;
}