【CCPC】2022绵阳站 D. Gambler's Ruin | 双指针

354 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情

题目链接

Problem - D - Codeforces

题目

image.png

题目大意

波波联队(BU)和波波城(BC)之间的足球比赛即将开始。你需要为每支球队设定赔率。

nn 个人准备在下注,第 ii 个人认为 BU 有 pip_i 的概率获胜。

如果你为 BU 设置赔率为 xx,为 BC 设置赔率为 yy,那么第 ii 个人:

  • 如果 pi×x1p_i\times x≥1,他将在 BU 上下注。
  • 如果 (1pi)×y1(1−p_i)\times y≥1,他将在 BC 上下注。

令在 BU 上下注的总金额为 sxsx 美元,在 BC 上下注的总额为 sysy 美元。如果 BU 最终赢得比赛,公司需要支付 sx×xsx\times x 美元;如果 BC 获胜,公司需要支付 sy×ysy\times y 美元。在最坏的情况下,利润是 sx+symax(sx×x,sy×y)sx+sy−\max(sx\times x,sy\times y)

合理设置 xxyy 的值,以在最坏的情况下实现利润最大化,输出最大的收益。

思路

让我们对于人的行为模式进行化简,原题意可以转化为对于第 ii 个人:

  • 如果 pi1xp_i≥\frac{1}{x},他将在 BU 上下注。
  • 如果 11ypi1−\frac{1}{y}≥p_i,他将在 BC 上下注。

为了不让人在两边都下注,应该使 1x11y\frac{1}{x}\ge 1−\frac{1}{y}

所以我们对所有人按 pp 属性排序后,对任一种可能成为最优解的答案,存在下标 i,ji,j 满足 i<ji<j 且对于每一个人 kk

  • kik\le i,他在 BC 上下注。
  • i<k<ji<k<j,他不下注。
  • jkj\le k,他在 BU 上下注。

显然 ii 变大时 jj 应该变小,双指针更新答案即可。

应对数据 pp 值相等的位置去重或进行相应处理。

代码

#include <iostream>
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include <map>
#include <vector>
#include <queue>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
using LL=long long;
const int N=1000001;
const LL mod=1000000007;
//const LL mod=998244353;
struct asdf{
	long double p;
	LL c;
	bool operator < (const asdf a) const
	{
		return p<a.p;
	}
}a[N];
int n;
long double sum[N];
long double getans(int i,int j)
{
	return sum[i]+sum[n]-sum[j-1]-max(sum[i]*(1.0/(1-a[i].p)),(sum[n]-sum[j-1])*(1.0/a[j].p));
}
LL solve()
{
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%Lf%lld",&a[i].p,&a[i].c);
	sort(a+1,a+1+n);
	int tot=1;
	for (int i=2;i<=n;++i) 
		if (a[i].p!=a[i-1].p) a[++tot]=a[i];
		else a[tot].c+=a[i].c; 
	n=tot;
	for (int i=1;i<=n;++i)
		sum[i]=sum[i-1]+a[i].c;
	sum[n+1]=sum[n];
	long double ans=0,x;
	for (int i=1,j=n;i<=n;++i)
	{
		x=getans(i,j);
		while (i<j-1&&getans(i,j-1)>=x)
		{
			j--;
			x=getans(i,j);
		}
		ans=max(ans,x);
	} 
	printf("%.15Lf",ans);
}
int main()
{
	int T=1;
	while (T--) solve();
	return 0;
}