利用势能函数求解期望

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.}$ 这种利用类势能函数来求解期望的一般满足几个条件:

  1. 从一个状态可以转向较多状态(类似随便从中选点),或者是状态很多,有时状态数会连 $\text{long} \  \text{long}$ 都爆,此时期望 $\text{DP}$ 难以转移,就可以考虑使用势能函数
  2. 对于一次转移(就是已经选出来了该次操作要用的,准备转移),式子较为简单,而且当前状态不会转移成自己(不然肯定会使得 $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;
}
posted @ 2020-10-17 12:12  一叶知秋‘  阅读(363)  评论(1编辑  收藏  举报