[2018.07.12 T1]B君的第一题

B君计划投注于甲乙两队的系列赛,通过精心计算,确保最终收益为固定值。面对最多2n-1场比赛,需在每场比赛后根据当前局势调整下注金额。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暂无链接

B君的第一题

【问题描述】

天台上的朋友让一让,让我先来。

世界杯开始了,B 君最近在关注一局季后赛。

在这局季后赛中,甲队和乙队将会进行至多 2n12n−1 场比赛,谁先取得 nn 场胜利,谁便会获得最终的胜利。并且之后的比赛不会继续进行。

B 君很看好甲队,为了计算方便,B 君希望下注 22n1 元,也就是说, 如果最终甲队先取得了nn 场胜利,B 君就会获得 22n1 元。如果最终乙队先取得了nn 场胜利,B 君就会失去 22n1 元。

但是事不如人愿,甲队和乙队水平旗鼓相当,每一场比赛,双方胜利的概率都是0.50.5,并且所有比赛的结果相互独立。

B 君对此坦然接受,认为自己获得 22n122n−1 元的概率是 0.50.5,失去22n122n−1 元的概率是 0.50.5, 期望收益是 00

但是赌场的庄家并不允许这样做,必须每一场比赛单独下注,分别进行结算。

B 君很失望,但是他发现,存在唯一一种下注方式,达到自己最初的目的。

即如果最终甲队获胜,B 君各场的收益之和为 22n1,如果最终乙队获胜,B 君各场收益之和为22n1−22n−1

比如对于n=2n=2 来说,B 君可以选择在第一场下注44元,在第二场下注 44 元,在第三场下注8 元。

这样如果甲队或乙队赢了前两场,B 君直接获得或失去了88 元。

如果前两场甲乙一胜一负,那么 B 君不赔不赚,决胜局下注 8 元。

也就是说,无论甲以什么方式获胜,B 君都会获得 88 元。无论乙以什么方式获胜, B 君都会失去8 元。

特别的,B 君可以在看到前ii 场的结果之后,再给出第 i+1 场的下注,而不需要一 开始就给出所有场的下注情况。

对于输入输出,B 君会先告诉你nn,然后你来告诉B 君第一场比赛应该如何下注, 接下来 B 君告诉你第一场的结果。

你来告诉 B 君第二场比赛应该如何下注,依次类推。如果 B 君告诉你的结果已经导致了甲队或者乙队取得了 n 场胜利。

你不需要对这次结果做任何回复。

因此你输出的数字个数,和B 君告诉你比赛结果的个数相同。

你可以下注负数,表示下注乙队获胜,这样如果甲队失败便可获得回报。

这并不是一个交互题,每个回答的答案是唯一的。
【输入格式】

第一行一个数字 nn,即上文中所描述的n

第二行是一些001,如果是00 表示甲队获胜,1表示乙队获胜。

保证这是一个合法的比赛过程,也就是说,当出现 00 的个数或 1 的个数达到nn 个之后, 便不会有更多输入了。

【输出格式】

输出若干个数字,一行一个。个数和输入的第二行数字个数相同,表示每场比赛的下注。

由于下注可能很大,你只需要输出模1000000007的结果即可。

即使想下注负数,也应该输出取模后的正余数。

可以证明下注的金额一定是整数。

可以证明解是唯一的。

【输入样例】

3
1 1 0 0 1

【输出样例】

12
12
8
16
32

【样例说明】

显然所有下注的和,要么是+22n1+22n−1,要么是22n1−22n−1

对于这场(12)+(12)+(8)+(16)+(32)=32=25(−12)+(−12)+(8)+(16)+(−32)=−32=−25

【输入样例】

3
0 1 0 0

【输出样例】

12
12
16
16

【样例说明】

(12)+(12)+(16)+(16)=32=25(12)+(−12)+(16)+(16)=32=25

你可以根据前两场的结果,来动态的决定第三场的下注。

输入的第二行长度并不确定,当输入nn0nn1时结束。

【数据范围】

对于100%100%的数据,满足1n1000001≤n≤100000

对于70%70%的数据,满足1n10001≤n≤1000

对于30%30%的数据,满足1n61≤n≤6

数据非常有梯度。

题解

以为数据有梯度是区分度很高的意思,部分分有就有,没有就没有,不存在强行造档的情况,所以考场代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    freopen("beijing.in","r",stdin);
    freopen("beijing.out","w",stdout);
    while(1)puts("sh*t");
}

然而出题人居然良心发现,“梯度”的意思居然是n=1,2,3n=1,2,3都有!,根据题目打个1,2,31,2,3的表都有1515分,令人窒息的操作。。。

唉,我dpdp还是太菜了,O(n2)O(n2)dpdp都想不出来。。。

先考虑O(n2)O(n2)dpdp吧,我们可以算算甲乙两队赢的概率,设dp[i][j]dp[i][j]为甲队胜ii场,乙队胜j场时,甲队取得最终胜利的概率,那么就有dp[i][j]=dp[i+1][j]+dp[i][j+1]2dp[i][j]=dp[i+1][j]+dp[i][j+1]2,即dp[i+1][j]dp[i][j]=dp[i][j]dp[i][j+1]dp[i+1][j]−dp[i][j]=dp[i][j]−dp[i][j+1],所以无论输赢,甲队的胜率变化是一样的。

初始情况dp[0][0]dp[0][0]胜率为1212,末状态为001,收益为22n1−22n−122n122n−1,胜率每变化1212,收益变化22n122n−1,那么对于一次变化ΔxΔx,我们就应该应该下注Δx1222n1=2Δx×22n1Δx1222n−1=2Δx×22n−1元。

然后我们就有了一个优秀的O(n2)dpO(n2)dp做法。

其实,获胜概率还可以用组合数算出来,考虑当甲队赢了ii场,乙队赢了j场时,对于剩下的res=2n1ijres=2n−1−i−j场比赛,如果甲队取得了nin−i或更多的场的胜利,就能获胜,所以甲队的胜率可以表示如下:

resk=ni(resk)2res∑k=n−ires(resk)2res

组合意义很明显,赢得niresn−i∼res场的情况比上所有情况。

似乎还是无从下手,考虑我们dpdp的思路,如果甲队多赢了一场,胜率将变为:

2n2ijk=ni1(2n2ijk)22n2ij∑k=n−i−12n−2−i−j(2n−2−i−jk)22n−2−i−j

如果乙队赢了:

2n2ijk=ni(2n2ijk)22n2ij∑k=n−i2n−2−i−j(2n−2−i−jk)22n−2−i−j

发现这两个式子的差只有一项:

(2n2ijni1)22n2ij(2n−2−i−jn−i−1)22n−2−i−j

代入dpdp得到的结论中,可知我们应下注:

(2n2ijni1)22n2ij×22n1=21+i+j(2n2ijni1)(2n−2−i−jn−i−1)22n−2−i−j×22n−1=21+i+j(2n−2−i−jn−i−1)

可以预处理组合数,也可以一乘一除得到下一项,复杂度O(n)O(n)

代码
#include<cstdio>
#define ll long long
using namespace std;
const int M=2e5+5,mod=1e9+7;
int inv[M],x,y,n,p;
ll C(int a,int b){ll ans=1;for(int i=0;i<b;++i)ans=ans*(a-i)%mod*inv[i+1]%mod;return ans;}
void in(){scanf("%d",&n);}
void ac()
{
    inv[1]=1;for(int i=2;i<=n+n;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    x=y=n;ll ans=C(x+y-2,x-1)*2%mod;
    while(x>0&&y>0)
    {
        printf("%lld\n",ans);scanf("%d",&p);
        ans=ans*2*inv[x+y-2]%mod;
        if(p)ans=ans*--y%mod;else ans=ans*--x%mod;
    }
}
int main(){in();ac();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值