题解:MX-2025-3S-T1

题面

MX-2025-3S-T1.棋子

A. 棋子

内容限制:1024 MB 时间限制:1000 ms 输入文件:cp.in 输出文件:cp.out
题目类型:传统 评测方式:文本比较

题目描述

给出一个 \(n\) 面骰子进行飞行棋,现在你距离终点有 \(n\) 的距离,每回合你会等概率投出 \([1, n]\) 中的一个数字 \(x\) ,向前走一步,如果没能走进终点且还有剩余步数,则会往反方向走剩余步数,同时如果你投出了数字 \(n\) ,则会免费获得额外的一次投掷次数(可以不带投掷 \(n\) 进行叠加),问期望进行多少回合走进终点,答案对 \(10^9 + 7\) 取模。

输入格式

第一行给出一个正整数 \(T\),表示数据组数。

之后对于每组数据,第一行给出正整数 \(n\)

输出格式

对于每组数据,输出一个整数,表示答案,答案对 \(10^9 + 7\) 取模。

样例

【样例1 输入】

5  
6  
8  
100  
5000  
100012300  

【样例1 输出】

166666673  
125000008  
570000103  
571405003  
359766921  

数据范围与提示

  • 对于 30% 的数据, \(6 \le n \le 10\)
  • 对于 60% 的数据, \(6 \le n \le 100\)
  • 对于 100% 的数据, \(6 \le n \le 10^9\)\(1 \le T \le 10^5\)

说在前面

1.回合:投掷若干次骰子直到符合结束回合条件

2.投掷骰子:题面给出的骰子是一个 \(n\) 面骰子,等概率扔出 \([1,n]\) 之间任意整数数字

3.移动:投骰子之后依据数字改变自身位置

通俗解释

(题目描述有点迷,看此处的分析即可)

(我们先不理会“投出数字 \(n\) 获得一次额外投掷次数”这个条件的干扰)

一道数学期望题,分析条件:

从位置 \(0\) 出发,终点是位置 \(n\),并进行若干次投骰子移动环节,求需要平均多少回合投骰子移动环节能走到位置\(n\)

投骰子移动:如果此时位置不是位置 \(n\) 而是位置 \(p\),则进入 \(1\) 回合投骰子环节,投出得到 \([1,n]\) 之间的整数 \(k\),然后移动到位置 \(pos\),并使经过回合数加 \(1\),移动规则如下


\[\begin{cases} p+k<=n时,pos=p+k\\ p+k>n时,pos=n-(p+k-n)=2n-p-k \end{cases} \]

对于 \(p+k>n\) 情况,就是从当前位置 \(p\) 向“前”走,本应该走到位置 \(p+k\),但最多走到位置 \(n\),于是从位置 \(n\) 向“后”把多余的步数走完,称为反弹机制。(此处的经过位置 \(n\) 不算到达终点,算是过犹不及)


移动过后,对新到达的位置 \(pos\) 进行分析:

1.若 \(pos\neq n\),则令当前位置 \(p\) 等于新位置 \(pos\) 即令 \(p=pos\),并开始下一轮投骰子

2.若 \(pos=n\),则到达终点点

\(\color{red}\Large \delta\) 经过我们一定的分析(自己大概分析一下),就能注意到(数学分析会用到这里的结论):

结论1.从位置 \(0\) 出发,到 \([1,n]\) 任意位置都是 \(\frac 1 n\) 的概率。

结论2.从位置 \([1,n-1]\) 出发,有 \(\frac 1 n\) 概率走到位置 \(n\),有 \(\frac {n-1} n\) 概率走到 \([1,n-1]\) 的位置(蕴含的信息:位置 \([1,n-1]\) 中任意位置之间等价)

数学分析

跟随上面的规则去走,只能知道一种特定的走到终点的方案,而不知道所有方案平均的回合数,用正常的概率无法得知,而用期望可以解决。期望即试验中概率加权平均数乘其结果的总和,后面选修会学到,我们这里大概应用,不多解释,用这道题先大概理解期望就行。

对期望举个简单的例子:投六面骰,期望(平均)的点数为

\[\frac 1 6\times (1+2+3+4+5+6) = 3.5 \]

我们把走到的位置看成状态,例如目前处在位置 \(13(13<n)\) 就是一种状态,此时走到终点的平均需要多少回合我们不知道,那么就假设平均需要 \((E_{13})\) 回合走到终点(注意这里的 \(E_{13}\) 不一定是整数,不解释),对于 \([0,n]\) 之间的每个位置 \(i\) 我们都假设他们到终点的平均回合数为 \(E_i\) 回合,我们最终的答案就是 \(E_0\)

目前我们的答案仍是未知,要对每个 \(E_i\) 之间的关系进行分析,结合通俗解释中最后一板块 \(\color{red}\delta\) 的结论,我们可以得出以下的初步数学结论:


\[\begin{cases} E_0=?\\ E_1=E_2=...=E_{n-1}=?\\ E_n=0 \end{cases} \]


根据通俗解释 \(\color{red}\delta\),我们可以发现位置 \([1,n-1]\) 中任意位置之间其实都是等价的,于是现在有三种状态:位置 \(0\),位置 \([1,n-1]\),位置 \(n\).

考虑如何在三种状态之间列出等量关系式子是容易的,我们令:

\(\large{\color{blue}{E_v}}=E_1=E_2=...=E_{n-1}\)

则根据通俗解释 \(\color{red}\delta\) 中结论 \(2\) ,可以知道,在位置 \([1,n-1]\):

1.有 \(\frac 1 n\) 概率走 \(1\) 步到终点;

2.有 \(\frac {n-1} n\) 概率走 \(1\) 步到位置 \([1,n-1]\),再走 \(E_v\) 步到终点

可以列出这样的式子:


\(E_v=\frac 1 n(1+E_n) + \frac {n-1} n (1+E_v)\)


上式化简可得 \(\color{blue}E_v=n\),即在位置 \([1,n-1]\) 平均 \(n\) 步走到终点。接下来我们找 \(E_0\)\(E_v\) 的关系,由通俗解释 \(\color{red}\delta\),在位置 \(0\)

1.有 \(\frac 1 n\) 概率走一步到终点;

2.有 \(\frac {n-1} n\) 概率走 \(1\) 步到位置 \([1,n-1]\),再走 \(E_v\) 步到终点

(其实与 \(E_v\) 式子同理)可以列出这样的式子:


\(E_0=\frac 1 n(1+E_n) + \frac {n-1} n (1+E_v)\)


照样化简得 \(\color{blue}E_0=n\)

最终答案

上面的 \(\color{blue}E_0=n\) 并不是最终答案,因为在最开始的时候我们说忽略“投出数字 \(n\) 获得一次额外次数”这个条件。

对这个条件简单理解一下(防止有些人卡壳太久),我们先区分一下回合和次数,原先在一个回合内只能投一次骰子,若投出数字 \(n\) ,则多投一次骰子,但不代表少走一个回合,而是这个回合多投一次骰子。特殊情况如下:

1.从位置 \(0\) 投出了数字 \(n\),算是 \(1\) 回合,只是这回合本可以投两次骰子,但投完第一次就达到了终点结束了。

2.反复投出数字 \(n\),那么这一回合一直不结束,直到达到终点或投出其他数字。

那么我们对上面关于答案的公式进行改变,先分析:

1.在位置 \(0\)\(\frac 1 n\) 概率投出 \(n\) 平均 \(1\) 回合解决

  1. \(\frac {n-1} n\) 概率投出 \([1,n-1]\) 到达位置 \([1,n-1]\) 平均 \(1+E_v\) 回合解决;

3.在位置 \([1,n-1]\)\(\frac 1 n\) 概率投出数字 \(n\) 但由于反弹机制,并没有到达终点而是多投一次,又花费 \(0\) 步无代价回到位置 \([1,n-1]\),再从位置 \([1,n-1]\) 出发平均需要 \(E_v\) 回合到达;

3.在位置 \([1,n-1]\) , \(\frac 1 n\) 概率投出直接到达终点需要的数字即平均 \(1\) 回合到达;

4.在位置 \([1,n-1]\) , \(\frac {n-2} n\) 概率投出其他数字,多花费当前回合的 \(1\) 回合代价,回到 \([1,n-1]\) 状态,平均 \(1+E_v\) 回合到达


\[\begin{cases} E_v=\frac 1 n(1+E_n) +\frac 1 n E_v + \frac {n-2} n(1+E_v)\\ E_0=\frac 1 n(1+E_n)+\frac {n-1} n(1+E_v) \end{cases} \]


化简整理得最终答案为 \(E_v=n-1\) , \(E_0=\frac 1 n+\frac {n-1} n \times n=\color{blue} \frac 1 n+n-1\)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int P = 1e9 + 7;
int T, n;
int mi(int x, int y, int mod){
	int ans = 1;
	while (y){
		if (y & 1)
		ans =(ans * x) % mod;
		x=(x * x)% mod, y >>= 1;
	}
	return ans;
}
signed main(){
	cin >> T;
	while (T--) cin >> n, cout << (n - 1 + mi(n, P - 2, P))% P<<'\n';
	return 0;
}
posted @ 2025-08-20 20:55  badn  阅读(10)  评论(0)    收藏  举报