AtCoder Beginner Contest 373 E题 How to Win the Election(二分套二分)

题目链接

AtCoder Beginner Contest 373 E

思路

很明显,答案是具有单调性的,我们考虑二分答案。

我们定义 s s s数组为 a a a数组从大到小排序后的前缀和数组。

对于排序后的第 i i i个数,我们假设给其增加 x x x票(通过二分答案确定),若是想要其当选,则将剩下的 k − x k-x kx票给其他候选者之后,比 a [ i ] + x a[i]+x a[i]+x多的不得大于 m m m个。

因此我们可以在前缀和数组上再次二分,直接判断 x x x是否可行即可。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, m, k;
int b[N], s[N];
struct node
{
	int a, id, ans;
} p[N];
void solve()
{
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++)
	{
		cin >> p[i].a;
		p[i].id = i;
		k -= p[i].a;
	}
	sort(p + 1, p + 1 + n, [&](node x, node y) {return x.a > y.a;});
	for (int i = 1; i <= n; i++)
	{
		s[i] = s[i - 1] + p[i].a;
		b[i] = p[n - i + 1].a;
	}
	for (int i = 1; i <= n; i++)
	{
		if (p[i].a == p[i - 1].a)
		{	//特殊情况
			p[i].ans = p[i - 1].ans;
			continue;
		}
		if (p[i].a + k < p[m].a)
		{
			p[i].ans = -1;
			continue;
		}
		if (i <= m)
		{
			int res = (m + 1 - i) * p[i].a - (s[m + 1] - s[i]) + (m + 1 - i);
			if (res > k)
				p[i].ans = 0;
			else
			{
				int low = 1, high = k;
				while (low < high)
				{
					int mid = low + high >> 1;
					auto check = [&](auto check, int x)->bool{
						int op = p[i].a + x;
						int tmpk = k - x;
						int idx = upper_bound(b + 1, b + 1 + n, op) - b;
						idx = n - idx + 1;
						int res = (m + 1 - idx - 1) * op - (s[m + 1] - s[idx]) + p[i].a + (m + 1 - idx - 1);
						return res <= tmpk;
					};
					if (check(check, mid))
					{
						low = mid + 1;
					}
					else high = mid;
				}
				p[i].ans = high;
			}
		}
		else
		{
			int low = 1, high = k;
			while (low < high)
			{
				int mid = low + high >> 1;
				auto check = [&](auto check, int x)->bool{
					int op = p[i].a + x;
					int tmpk = k - x;
					int idx = upper_bound(b + n - m + 1, b + 1 + n, op) - b;
					idx = n - idx + 1;
					int res = (m - idx) * op - (s[m] - s[idx]) + (m - idx);
					return res <= tmpk;
				};
				if (check(check, mid))
				{
					low = mid + 1;
				}
				else high = mid;
			}
			p[i].ans = high;
		}

	}
	sort(p + 1, p + 1 + n, [&](node x, node y) {return x.id < y.id;});
	for (int i = 1; i <= n; i++)
	{
		cout << p[i].ans << " ";
	}
	cout << endl;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int test = 1;
	// cin >> test;
	for (int i = 1; i <= test; i++)
	{
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值