题解 一道有意思的题——来自生活的思考
UPD 2022.7.27:发现了另解。
题面
题解
Part 1
由于两人之间的猜拳可能有平局,我们尝试把平局对答案的影响消去。
考虑两人期望多少次分出胜负,\(i\) 次分出胜负的概率为 \((\dfrac{1}{3})^{i-1}\times\dfrac{2}{3}\)。
因此两人期望分出胜负的次数:\(\sum_{i=1}^{+\infty}i\times(\dfrac{1}{3})^{i-1}\times\dfrac{2}{3}=\dfrac{3}{2}\)。
这样,原问题就转化为:在原有规则上,无平局,两人一组的猜拳期望要进行多少回合。
新问题的答案乘上 \(\dfrac{3}{2}\) 就是原问题的答案。
Part 2
因为要求的是猜拳总期望数,只与 \(n\) 和 \(m\) 有关,而与每个人无关,故我们可以假设每个人相同。
一次猜拳发生,需要两个人的等级相同。每次猜拳相当于在等级相同的人中选 \(2\) 个,将他们一个升 \(1\) 级,一个降 \(1\) 级。
我们考虑不记录每个人的等级,而是记录每个等级有多少人。
那么每次猜拳,相当于选一个至少 \(2\) 个人的等级进行操作。
我们记录一个序列 \(\{a_i\}\),表示第 \(i\) 个等级有多少人。
从总体上来看,最终结果有两种:
-
\(\forall i\),\(1\leq i\leq n\),\(a_i=1(n\lt m)\)。
-
\(\forall i\),\(1\leq i\leq m-1\),\(a_i=1\),\(a_m=n-m+1(n\ge m)\)。
发现第一种情况只用到了 \(i\leq n\) 的 \(a_i\),故我们可将 \(m\) 缩小为 \(n\) 转换为第二种情况。
无论哪种情况,对于两个操作,如果不互相依赖,就可以交换顺序。
这时我们发现新问题的答案是一个定值,故考虑构造一种方便计算次数的情况。
Part 3
首先一开始的情况是 \(a_1=n\),\(\forall i\),\(2\leq i\leq m\),\(a_i=0\)(\(n\underbrace{0\cdots 0}_{2\sim m}\))。
假设已有状态 \(a_1=t\),\(\forall 2\le i\le k-1\),\(a_i=1\),\(a_{k}=0\),\(a_{k+1}=1\),\(\forall k+2\le i\le m\),\(a_i=0\)(\(t\underbrace{1 \cdots 1}_{2\sim k-1}01\underbrace{0\cdots 0}_{k+2\sim m}\))。
现在要把它变为 \(a_1=t-1\),\(\forall 2\le i\le k\),\(a_i=1\),\(a_{k+1}=0\),\(a_{k+2}=1\),\(\forall k+3\le i\le m\),\(a_i=0\)(\((t-1)\underbrace{1 \cdots 1}_{2\sim k}01\underbrace{0\cdots 0}_{k+3\sim m}\))。
先变成 \(a_1=t-1\),\(\forall 2\le i\le k+1\),\(a_i=1\),\(\forall k+2\le i\le m\),\(a_i=0\)(\((t-1)\underbrace{1 \cdots 1}_{2\sim k+1}\underbrace{0\cdots 0}_{k+2\sim m}\))。这要 \(\sum_{i=1}^{k-1}i=\dfrac{k(k-1)}{2}\) 次。
再变成 \(a_1=t-1\),\(\forall 2\le i\le k\),\(a_i=1\),\(a_{k+1}=0\),\(a_{k}=1\),\(\forall k+3\le i\le m\),\(a_i=0\)(\((t-1)\underbrace{1 \cdots 1}_{2\sim k}01\underbrace{0\cdots 0}_{k+3\sim m}\))。这要 \(k+1\) 次。
也就是说,上述操作使用总次数为 \(\dfrac{k(k-1)}{2}+k+1\)。
而从初始状态 \(a_1=n\),\(\forall i\),\(2\leq i\leq m\),\(a_i=0\)(\(n\underbrace{0\cdots 0}_{2\sim m}\))变化为 \(a_1=n-1\),\(a_2=1\),\(\forall i\),\(3\leq i\leq m\),\(a_i=0\)(\((n-1)1\underbrace{0\cdots 0}_{3\sim m}\))要 \(1\) 次。
所以从初始状态变为 \(a_1=n-m+2\),\(\forall i\),\(2\leq i\leq m-2\),\(a_i=1\),\(a_{m-1}=0\),\(a_m=1\)(\((n-m+2)\underbrace{1\cdots1}_{2\sim m-2}01\)) 一共要 \(1+\sum_{i=1}^{m-2}(\dfrac{k(k-1)}{2}+k+1)=1+\dfrac{1}{2}(\sum_{i=1}^{m-2}k^2+\sum_{i=1}^{m-2}k)+\sum_{i=1}^{m-2}1\)
\(=1+\dfrac{1}{2}(\dfrac{(m-2)(m-1)(2m-3)}{6}+\dfrac{(m-1)(m-2)}{2})+(m-2)\) 次。
Part 4
之后就比较简单了,每次由 \(a_1=t\),\(\forall i\),\(2\leq i\leq m-2\),\(a_i=1\),\(a_{m-1}=0\),\(a_m=n-m-t+3\)(\(t\underbrace{1\cdots1}_{2\sim m-2}0(n-m-t+3)\))
变为 \(a_1=t-1\),\(\forall i\),\(2\leq i\leq m-1\),\(a_i=1\),\(a_m=n-m-t+3\)(\((t-1)\underbrace{1\cdots1}_{2\sim m-1}(n-m-t+3)\)),需要 \(\sum_{i=1}^{m-2}i\) 次。
再将其变为 \(a_1=t-1\),\(\forall i\),\(2\leq i\leq m-2\),\(a_i=1\),\(a_{m-1}=0\),\(a_m=n-m-t+4\)(\((t-1)\underbrace{1\cdots1}_{2\sim m-2}0(n-m-t+4)\))需要 \(m-1\) 次。
因此之后每将 \(a_1\) 减少 \(1\),\(a_m\) 加上 \(1\),一共要 \(\sum_{i=1}^{m-1}i=\dfrac{m(m-1)}{2}\) 次。
由于中间过程出现了 \(t-2\),因此 \(t\) 至少为 \(2\)。
不断重复该过程,直至 \(a_1=2\),一共要 \(\dfrac{(n-m)m(m-1)}{2}\) 次。
之后再来一遍操作把目前数列变为最终状态,需要 \(\sum_{i=1}^{m-2}i=\dfrac{(m-1)(m-2)}{2}\) 次。
因此答案为 \(1+\dfrac{1}{2}(\dfrac{(m-2)(m-1)(2m-3)}{6}+\dfrac{(m-1)(m-2)}{2})+(m-2)+\dfrac{(n-m)m(m-1)}{2}+\dfrac{(m-1)(m-2)}{2}\)。
太长了,这里不做化简,留给读者当课后练习。
原问题的答案即为该式乘上 \(\dfrac{3}{2}\)。
代码
随机数模拟游戏过程
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int K=5000000;
int n,m,p,q,x,y,T=0,tot,sum;
LL ans,res=0;
int cnt[1000002],lev[1000002];
inline void solve()
{
for(int i=1;i<=n;++i)cnt[i]=0;
cnt[0]=n,tot=0,sum=1;
for(int i=1;i<=n;++i)lev[i]=0;
QAQ:if(tot==p && sum==q)return ;
do x=rand()%n+1,y=rand()%n+1;while(x==y || lev[x]==m-1 || lev[y]==m-1 || lev[x]!=lev[y]);
--cnt[lev[x]];if(!cnt[lev[x]])--sum;
--cnt[lev[y]];if(!cnt[lev[y]])--sum;
++ans,--lev[x],++lev[y],lev[x]=max(lev[x],0);
if(!cnt[lev[x]])++sum;++cnt[lev[x]];
if(!cnt[lev[y]])++sum;++cnt[lev[y]];
if(lev[y]==m-1)++tot;
if(T%K==1)
{
printf(" - round %lld : %d VS %d , %d wins\n points : ",ans,x,y,y);
for(int i=1;i<=n;++i)printf("%d%c",lev[i],i==n? '\n':' ');
}
goto QAQ;
}
int main()
{
srand(time(NULL)),scanf("%d%d",&n,&m),p=max(0,n-m+1),q=min(n,m);
while(++T)
{
ans=0,solve(),res+=ans;
if(T%K==1)printf("Round %d : %lld Total : %lld Average : %.20lf\n",T,ans,res,1.0*res/T);
}
return 0;
}
正解
#include<bits/stdc++.h>
int main()
{
for(long long n,m,ans;;)scanf("%lld%lld",&n,&m),m=std::min(m,n),ans=1+((m-2)*(m-1)*(2*m-3)/6+(m-1)*(m-2)/2)/2+(m-2)+(n-m)*m*(m-1)/2+(m-1)*(m-2)/2,printf("%lld\n",ans);
return 0;
}
鸣谢
感谢 defkaeru,Konnyaku_LXZ,zc_li 几位同学与我一起交流,讨论本题解法,该做法离不开大家的共同努力!