算法分析
二分答案
的数据范围,我们显然是不可能去模拟每一轮的
那么其实这一轮一轮的循环,只需要模拟最后一轮就可以了,因为之前每一轮的变化其实就是
假设当前轮为 ,
,那么前后轮之间其实并没有啥影响,那么我们只需要去二分最后一轮是在什么时候,再模拟一遍最后一轮的过程就可以了。
那么这个二分该怎么写呢
bool check(ll x)
{
ll res = 0;
for(int i = 1;i <= n;i ++)
{
res += min(x,1ll * a[i]);
}
return res <= k;
}
在这里 表示当前二分的轮数,
表示走
轮,同学们会打
份饭,倘若
说明还可以还可以继续打饭
二分解决以后只需要解决最后一轮的模拟了
deque<int> q;
for(int i = 1;i <= n;i ++) k -= min(round,1ll * a[i]),a[i] -= min(round,1ll * a[i]);
for(int i = 1;i <= n;i ++) if(a[i]) q.push_back(i);
while(k --)
{
a[q.front()] --;
if(a[q.front()]) q.push_back(q.front());
q.pop_front();
}
为剩下还能打多少饭,将
修正为经过
后还剩下多少,然后就模拟打饭的过程就完事了
AC code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int a[N];
ll n,k,sum = 0;
bool check(ll x)
{
ll res = 0;
for(int i = 1;i <= n;i ++)
{
res += min(x,1ll * a[i]);
}
return res <= k;
}
int main()
{
cin >> n >> k;
for(int i = 1;i <= n;i ++) scanf("%d",&a[i]),sum += a[i];
if(sum < k) puts("-1");
else if(sum == k) puts("");
else
{
ll l = 0,r = 1e9;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1 ;
}
ll round = l;
deque<int> q;
for(int i = 1;i <= n;i ++) k -= min(round,1ll * a[i]),a[i] -= min(round,1ll * a[i]);
for(int i = 1;i <= n;i ++) if(a[i]) q.push_back(i);
while(k --)
{
a[q.front()] --;
if(a[q.front()]) q.push_back(q.front());
q.pop_front();
}
for(auto x : q) printf("%d ",x);
}
return 0;
}