题解 一道有意思的题——来自生活的思考

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\) 个等级有多少人。

从总体上来看,最终结果有两种:

  1. \(\forall i\)\(1\leq i\leq n\)\(a_i=1(n\lt m)\)

  2. \(\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;
}

鸣谢

感谢 defkaeruKonnyaku_LXZzc_li 几位同学与我一起交流,讨论本题解法,该做法离不开大家的共同努力!

posted @ 2021-07-05 19:43  18Michael  阅读(59)  评论(0编辑  收藏  举报