描述
YJC最近在研究世界杯,他拿一半财产压了德国队,另一半财产压了阿根廷队,结果可想而知。YJC表示非常angry,于是又开始研究博彩公司的盈利原理。
假设在世界杯决赛前,有n个人参与了赌博。第i个人认为法国队赢的概率是p[i]p[i],克罗地亚队赢的概率是1−p[i]。对于每一只球队,如果根据博彩公司给出的赔率第i个人的期望收益非负,则他会给这只球队下注a[i]元(设赔率为x,某人下注了y元,如果他赢了可以返还x∗y元,他输了则会返还0元。不论输赢,下注的钱均不返还)。如果两只球队期望收益均非负,则他会给两只球队各下注a[i]元。
现在博彩公司要决定法国队和克罗地亚队的赔率,使得在自身收益最小的胜负情况下的收益最大。
输入格式
第一行输入一个数n,表示总人数。
接下来n行,每行两个实数a[i]和p[i],均保留不超过6位小数。
输出格式
一个实数,表示无论比赛结果如何,博彩公司最小收益的最大值,保留66位小数。
样例1
样例输入
3
1 0.1
2.233 0.1
3.666 0.1
样例输出
0.000000
题面还是很鬼畜的
我们可以发现当赔率大于
1p[i]
1
p
[
i
]
时,会下注
我们同时可以发现,两个队伍的投注是独立的
分开处理。(当然也可以在一起排序分前缀后缀做)
分别按照
1p[i]
1
p
[
i
]
排序
我们用
sum[i]
s
u
m
[
i
]
表示赔率满足前
i
i
个人时前个人的下注之和
我们用
lose[i]
l
o
s
e
[
i
]
表示球赛输时前
i
i
个人下注亏的钱
考虑一个的暴力
枚举两场比赛的赔率(我们显然可以发现当一个赔率在
[1p[i],1p[i+1])
[
1
p
[
i
]
,
1
p
[
i
+
1
]
)
之间时,我门肯定选
1p[i]
1
p
[
i
]
最优)
这样的话我们只要枚举两场比赛的赔率
对所有的
min(suma[i]−loseb[j],sumb[j]−losea[i])
m
i
n
(
s
u
m
a
[
i
]
−
l
o
s
e
b
[
j
]
,
s
u
m
b
[
j
]
−
l
o
s
e
a
[
i
]
)
取个
max
m
a
x
即可
考虑优化,考虑,当
i
i
,确定时,我们对于每个,我们要尽可能的缩小两边的差距,因为
lose[i],sum[i]
l
o
s
e
[
i
]
,
s
u
m
[
i
]
都是递增的,所以肯定是我们让小的增加,让大的减少。
这样的话我们就可以取出
j
j
(也就是)中可能的对局,然后我们二分查找这个过程就行了。
实际上理论时间复杂度已经到最优了,应为排序就需要
nlogn
n
l
o
g
n
的时间,但是后面这个过程实际上可以做到
O(n)
O
(
n
)
,我们可以发现,实际上两个数组都是满足这个单调性的。也就是说如果
i
i
移动了一位,移动的方向是固定的!
感性的想
i
i
越大,下注的人越多,输的时候亏的越多,那我们就需要更多的人投注
j
j
来弥补这个亏损。同时也不能太多,不能让输的时候亏损过大。
相当于维护一个平衡,也就是两边同时滑
二分代码如下:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
int n , nn;
const int INF = 0x7fffffff;
double Max_a,Max_b;
double sum_a[1001000] , sum_b[1001000];
double lose_a[1001000] , lose_b[1001000];
struct point{
double x , y;
}tmp[1001000];
struct node{
double p , num;
int id;
}a[1001000],b[1001000];
int read()
{
int sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(flag) return sum;
else return -sum;
}
bool mycmp (node a,node b)
{
return a.p < b.p;
}
void init()
{
n = read();
rep(i,1,n)
{
double x , p;
scanf("%lf%lf",&x,&p);
a[i].num = b[i].num = x;
a[i].id = b[i].id = i;
if(p != 0) a[i].p = 1.0/p;
else a[i].p = (double) INF;
p = 1.0 - p;
if(p != 0) b[i].p = 1.0/p;
else b[i].p = (double) INF;
}
sort(a + 1,a + n + 1,mycmp);
sort(b + 1,b + n + 1,mycmp);
return;
}
bool mycmp1(point a,point b)
{
return a.x < b.x ||
(a.x == b.x && a.y < b.y);
}
int s[1001000] , top;
void pre()
{
rep(i,1,n)
{
tmp[i].x = sum_b[i];
tmp[i].y = lose_b[i];
}
sort(tmp + 1,tmp + n + 1,mycmp1);
s[++top] = 1;
rep(i,2,n)
{
while(top > 0 && tmp[i].y < tmp[s[top]].y) top--;
s[++top] = i;
}
int st = 1;
rep(i,st,top)
tmp[i-st+1].x = tmp[s[i]].x , tmp[i-st+1].y = tmp[s[i]].y;
nn = top - st + 1;
return;
}
void work()
{
rep(i,1,n)
{
sum_a[i] += (a[i].num+sum_a[i-1]);
if(a[i].p == a[i + 1].p)
{
lose_a[i] = (double) INF;
continue;
}
lose_a[i] = sum_a[i] * (a[i].p - 1);
}
rep(i,1,n)
{
sum_b[i] += (b[i].num+sum_b[i-1]);
if(b[i].p == b[i + 1].p)
{
lose_b[i] = (double) INF;
continue;
}
lose_b[i] = sum_b[i] * (b[i].p - 1);
}
pre();
return;
}
void solve()
{
double Max = 0;
rep(i,1,n)
{
int l = 1 , r = nn;
while(l + 1 < r)
{
int mid = (l + r)/2;
Max = max(Max,min(sum_a[i] - tmp[mid].y , tmp[mid].x - lose_a[i]));
if(sum_a[i] - tmp[mid].y < tmp[mid].x - lose_a[i])
r = mid;
else l = mid;
}
Max = max(Max,min(sum_a[i] - tmp[l].y , tmp[l].x - lose_a[i]));
Max = max(Max,min(sum_a[i] - tmp[r].y , tmp[r].x - lose_a[i]));
}
printf("%.6lf",Max);
return;
}
int main()
{
init();
work();
solve();
return 0;
}