利用势能函数求解期望
2023.3.31 UPD: 该博客已废除,请转到《鞅与一类关于停时的概率与期望问题》学习笔记
2021.7.6 UPD: 笔者做题时找到了更学术的博客(论文) ,发现自己错的太多了qwq,真正要学习的还是看这个吧: 势函数和鞅的停时定理
先来看一道题:CF1025G
貌似递推写不太出来,那怎么弄呢?
设每个状态 $S$ 都有它的函数值 $F(S)$ ,且对于转移到其它的 $S'$ , $F(S')=F(S)-1$
这个时候如果我们能让 $F(S_{end})$ 无法再转移(就是值最大),那么 $ans=F(S_{end})-F(S_{begin})$
既然是要让 $F(S_{end})$ 无法转移,那应当使 $F(S_{end})$ 的值最大
可以设 $F(S)=\sum f(a_i)$ ,至于 $a_i$ 是什么就要看具体的题目,但是这些 $a_i$ 要能准确地描述一种状态
然后对于几种转移列出式子,然后求解 $f(x)$ 就可以了
注意一点,式子中一般都会出现 $f(c)$ ( $c$ 是常数)
因为我们最后是求差,所以就像势能一样,可以随意定义初值,将 $f(c)=0$ 即可
最后就求出 $F(S_{begin})-F(S_{end})$ 就可以了
$\text{P.S.}$ 这种利用类势能函数来求解期望的一般满足几个条件:
- 从一个状态可以转向较多状态(类似随便从中选点),或者是状态很多,有时状态数会连 $\text{long} \ \text{long}$ 都爆,此时期望 $\text{DP}$ 难以转移,就可以考虑使用势能函数
- 对于一次转移(就是已经选出来了该次操作要用的,准备转移),式子较为简单,而且当前状态不会转移成自己(不然肯定会使得 $f(x)+1=f(x)$ ),而且一般对于 $f$ 要能写出通项
------------
对于这道题,我们让 $cnt_i$ 是跟在第 $i$ 个选中的点的后面的未选中的点
我们设该操作选择的两个选中点的后面分别有 $a$ 个点和 $b$ 个点
所以可列出式子:
$$f(a)+f(b)-1=\frac{1}{2}[(f(a+1)+b \times f(0))+(f(b+1)+a \times f(0))]$$
注意到这里出现了 $f(0)$ ,将其设为 $0$ ,所以可以转化为
$$f(a)+f(b)-1=\frac{1}{2}(f(a+1)+f(b+1))$$
很明显,$a$ 与 $b$ 不会相互影响(因为要满足所有 $a$ ,$b$ ),所以化为一般式(把 $a$ , $b$ 都看成 $x$)
$$f(x)-\frac{1}{2}=\frac{1}{2}f(x+1)$$
即
$$f(x)=2f(x-1)-1=\sum\limits_{i=0}^{x-1} -2^{i}=1-2^{x}$$
然后显然 $F(S_{end})=f(n-1)=1-2^{n-1}$ (因为只有一个选中的点)
直接求解即可
$code$ :
#include<cstdio> #include<cctype> #define maxn 555 #define mod 1000000007 inline int read(){ int r=0,f=0; char c; while(!isdigit(c=getchar()))f|=(c=='-'); while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar(); return f?-r:r; } int n,f[maxn]; long long ans,p[maxn]; int main(){ n=read(); p[0]=1; for(int i=1;i<=n;i++){ int mast=read(); if(~mast)f[mast]++; p[i]=p[i-1]*2%mod; } ans=p[n-1]-1; for(int i=1;i<=n;i++) (ans+=mod-p[f[i]]+1)%=mod; printf("%lld\n",ans); return 0; }