[北大集训 2021] 随机游走 题解

前言

又是随机游走?

题目链接:洛谷

题目分析

看到加边,可能性太多了。但是为了让步数最大化,我们可以贪心地想,肯定要往前面连,而且越前面要走的期望步数肯定越大。并且,我们不会浪费边在终点上。于是,题目转变成了 1n1 连向起点 1 连若干条边,使得随机游走到终点的期望步数最大。

那要如何分配这 m 条边到 1n1 个点呢?考虑假设已知第 i 个点向 1 连了 di 条边,求期望步数。设 fi 为到了 i,还要期望多少步走到终点,显然 fn=0。开始喜闻乐见的推式子环节:

fi=1di+1fi+1+didi+1f1+1

n1 向前递推。

fn1=1dn1+1fn1+1+dn1dn1+1f1+1=dn1dn1+1f1+1

推到 n2

fn2=1dn2+1fn1+dn2dn2+1f1+1=1dn2+1(dn1dn1+1f1+1)+dn2dn2+1f1+1=1dn2+1dn1dn1+1f1+dn2dn2+1f1+1dn2+1+1=dn1+dn2(dn1+1)(dn2+1)(dn1+1)f1+(dn2+1)+1dn2+1=(dn2+1)(dn1+1)1(dn2+1)(dn1+1)f1+(dn2+1)+1dn2+1

再推到 n3

fn3=1dn3+1fn2+dn3dn3+1f1+1=1dn3+1((dn2+1)(dn1+1)1(dn2+1)(dn1+1)f1+(dn2+1)+1dn2+1)+dn3dn3+1f1+1=(dn2+1)(dn1+1)1(dn3+1)(dn2+1)(dn1+1)f1+(dn2+1)+1(dn3+1)(dn2+1)+dn3dn3+1f1+1=dn3(dn2+1)(dn1+1)+(dn2+1)(dn1+1)1(dn3+1)(dn2+1)(dn1+1)f1+(dn3+1)(dn2+1)+(dn2+1)+1(dn3+1)(dn2+1)=(dn3+1)(dn2+1)(dn1+1)1(dn3+1)(dn2+1)(dn1+1)f1+(dn3+1)(dn2+1)+(dn2+1)+1(dn3+1)(dn2+1)

找到一些规律,尝试去证明。假设对于 i+1 满足:

fi+1=j=i+1n1(dj+1)1j=i+1n1(dj+1)f1+j=i+1n2k=jn2(dk+1)+1j=i+1n2(dj+1)

显然该式对于 n1 成立。尝试用归纳法推到 i

fi=1di+1(j=i+1n1(dj+1)1j=i+1n1(dj+1)f1+j=i+1n2k=jn2(dk+1)+1j=i+1n2(dj+1))+didi+1f1+1=j=i+1n1(dj+1)1j=in1(dj+1)f1+j=i+1n2k=jn2(dk+1)+1j=in2(dj+1)+didi+1f1+1=j=in1(dj+1)1j=in1(dj+1)f1+j=in2k=jn2(dk+1)+1j=in2(dj+1)

所以上式对于所有 i 均成立。考虑边界,推到 i=1 的时候是一个方程。

f1=j=1n1(dj+1)1j=1n1(dj+1)f1+j=1n2k=jn2(dk+1)+1j=1n2(dj+1)

解方程。

(j=1n1(dj+1))f1=(j=1n1(dj+1)1)f1+(dn1+1)(j=1n2k=jn2(dk+1)+1)

f1=j=1n2k=jn1(dk+1)+(dn1+1)

f1=j=1n1k=jn1(dk+1)

于是我们可以很方便地求出期望步数,即 f1。但是我们还是不知道最优的 d 如何分配,考虑打表找规律。在此之前,我们不妨试着找一找 d 的性质。

首先,d 一定是单调不降的。因为显然,放在后面会给更多的 提供贡献,从而使 f1 更大。

略证:

t[1,n2],dt>dt+1,考虑原先的 f1

f1=j=1n1k=jn1(dk+1)=j=t+2n1k=jn1(dk+1)+(dt+1+1)k=t+2n1(dk+1)+j=1t((dt+1)(dt+1+1)k=jt1(dk+1)k=t+2n1(dk+1))

不妨交换 dtdt+1

f1=j=t+2n1k=jn1(dk+1)+(dt+1)k=t+2n1(dk+1)+j=1t((dt+1+1)(dt+1)k=jt1(dk+1)k=t+2n1(dk+1))

显然,因为 dt>dt+1,所以交换后的 f1 更优。我们可以不断进行这个过程,直到 d 有序,即单调不降,f1 最大。

证毕。

其次……没其次了,打表吧。

while True:
n, m = map(int, input().split())
res = [0] * n
ans = [0] * n
maxx = 0
def dfs(now, tot, x):
global maxx, ans
if now == n - 1:
res[n - 1] = tot
tmp = 0
mul = 1
for i in range(n - 1, 0, -1):
mul *= res[i] + 1
tmp += mul
if tmp > maxx:
maxx = tmp
ans = res[::]
return
i = x
while i * (n - now) <= tot:
res[now] = i
dfs(now + 1, tot - i, i)
i += 1
dfs(1, m, 0)
print(' '.join(map(str, ans[1:])))

以上是打表程序。考虑 n=20 时,不断加边对最优 d 的分配造成的影响。

m = 1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
m = 2: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
......
m = 18: 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
m = 19: 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2
m = 20: 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2
......
m = 35: 0 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
m = 36: 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
m = 37: 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
m = 38: 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3
......
m = 54: 1 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
m = 55: 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
m = 56: 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
m = 57: 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4
......

发现,当 m<n1 时,加边是从 n1 开始放到 2,然后当 mn1 时,从 n1 开始放到 1,如此往复,像是在不断从右往左往序列上刷。感性理解,具体证明交给读者。

Update on 2024.6.20 更新了进一步的证明

下证任意相邻两数之差不超过 1

证明:

t[1,n2],dt+2dt+1,证明 dtdt+1,dt+1dt+11 后不劣。容易发现这样操作是合法的。

对于原先的 f1

f1=j=1n1k=jn1(dk+1)=j=t+2n1k=jn1(dk+1)+(dt+1+1)k=t+2n1(dk+1)+j=1t((dt+1)(dt+1+1)k=jt1(dk+1)k=t+2n1(dk+1))=j=t+2n1k=jn1(dk+1)+(dt+1+1)k=t+2n1(dk+1)+(dt+1)(dt+1+1)j=1tk=jt1(dk+1)k=t+2n1(dk+1)=j=t+2n1k=jn1(dk+1)+(dt+1+1+(dt+1)(dt+1+1)j=1tk=jt1(dk+1))k=t+2n1(dk+1)

后来的 f1,记作 f1

f1=j=t+2n1k=jn1(dk+1)+(dt+1+(dt+2)dt+1j=1tk=jt1(dk+1))k=t+2n1(dk+1)

那么考虑作差:

f1f1=((dt+1+(dt+2)dt+1j=1tk=jt1(dk+1))(dt+1+1+(dt+1)(dt+1+1)j=1tk=jt1(dk+1)))k=t+2n1(dk+1)=(((dt+2)dt+1(dt+1)(dt+1+1))j=1tk=jt1(dk+1)1)k=t+2n1(dk+1)=((dt+1dt1)j=1tk=jt1(dk+1)1)k=t+2n1(dk+1)

由于 dt+1dt2,所以 dt+1dt11。考虑到 j=1t 里至少有一个 k=tt1=1,则 (dt+1dt1)j=1tk=jt1(dk+1)1,那么有 f1f10,也就是 f1 是不劣于 f1 的。
如此操作,直至 t[1,n2],dt+1dt+1

有了如上证明,发现 t[1,n2],dt+1dt{0,1}。有了这个性质,接下来的证明最优性就简单啦。交给读者自己证明。(中考考完再来补坑)

那么,我们分别考虑两种情况即可。

情况一

m<n1 时,dnmn1=1,其他位置 d 均为 0。此时有:

f1=j=1n1k=jn1([knm]+1)=j=nmn1k=jn12+(nm1)k=nmn12=j=nmn12nj+(nm1)2m=2m+12+(nm1)2m=2m+12m2+(nm)2m=2m2+(nm)2m=(nm+1)2m2

快速幂单次 Θ(logm) 解决。

情况二

mn1 时,d1=m(n2)n1m 除去第一次刷的 n2,和 d1 次的刷完整个序列,还剩下 m=m(n2)(n1)d1 次从 n1 往左刷,故 d2n1m=d1+1dnmn1=d1+2。然后推式子。

f1=j=1n1k=jn1(dk+1)=k=1n1(dk+1)+j=2n1k=jn1(dk+1)=(d1+1)((d1+1)+1)(n1m)2+1((d1+2)+1)(n1)(nm)+1+j=2n1k=jn1(dk+1)=(d1+1)(d1+2)nm2(d1+3)m+j=2n1k=jn1(dk+1)=(d1+1)(d1+2)nm2(d1+3)m+j=2n1mk=jn1(dk+1)+j=nmn1k=jn1(dk+1)=(d1+1)(d1+2)nm2(d1+3)m+j=2n1m((d1+2)(n1m)j+1(d1+3)m)+j=nmn1k=jn1(dk+1)=(d1+1)(d1+2)nm2(d1+3)m+(d1+3)mj=2n1m(d1+2)(n1m)j+1+j=nmn1k=jn1(dk+1)=(d1+1)(d1+2)nm2(d1+3)m+(d1+3)mj=1(n1m)2+1(d1+2)j+j=nmn1k=jn1(dk+1)=(d1+1)(d1+2)nm2(d1+3)m+(d1+3)m(d1+2)n1m(d1+2)d1+1+j=nmn1k=jn1(dk+1)=(d1+1)(d1+2)nm2(d1+3)m+(d1+3)m(d1+2)n1m(d1+2)d1+1+j=nmn1k=jn1(d1+3)=(d1+1)(d1+2)nm2(d1+3)m+(d1+3)m(d1+2)n1m(d1+2)d1+1+j=nmn1(d1+3)nj=(d1+1)(d1+2)nm2(d1+3)m+(d1+3)m(d1+2)n1m(d1+2)d1+1+j=1m(d1+3)j=(d1+1)(d1+2)nm2(d1+3)m+(d1+3)m(d1+2)n1m(d1+2)d1+1+(d1+3)m+1(d1+3)d1+2

其中,d1m 在上文已经算出,故该式可以在 Θ(logm) 的时间内算出。

代码

注意特判 n=1 的情况。

def fpow(a, b, mod):
if b < 0:
return 1
res = 1
base = a % mod
while b:
if b & 1:
res = res * base % mod
base = base * base % mod
b >>= 1
return res
def inv(x, mod):
return fpow(x, mod - 2, mod)
def main():
n, m, mod = map(int, input().split())
if n == 1:
print(0)
return
if m < n - 1:
pr = fpow(2, m, mod)
print((pr * (n - m + 1) + mod - 2) % mod)
return
d1 = (m - (n - 2)) // (n - 1)
M = m - (n - 2) - (n - 1) * d1
S1 = (d1 + 1) * fpow(d1 + 2, n - M - 2, mod) * fpow(d1 + 3, M, mod)
S2 = fpow(d1 + 3, M, mod) * (fpow(d1 + 2, n - 1 - M, mod) - (d1 + 2) + mod) * inv(d1 + 1, mod)
S3 = (fpow(d1 + 3, M + 1, mod) - (d1 + 3) + mod) * inv(d1 + 2, mod)
S1 %= mod
S2 %= mod
S3 %= mod
print((S1 + S2 + S3) % mod)
if __name__ == '__main__':
t = int(input())
for i in range(t):
main()

后记

python 写的目的是因为教练讲题时用 python 打表唤起了我的回忆?

posted @   XuYueming  阅读(125)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示