Luogu3750 [六省联考2017]分手是祝愿

https://www.luogu.com.cn/problem/P3750

期望\(DP\)

运用贪心的策略,如果我们想要尽快关闭所有的灯,显然从编号大的到编号小的关更优

那么我们可以计算出关灯的最小次数\(cnt\)

如果\(cnt \le k\),显然答案就是\(cnt\)

如果\(cnt > k\),我们考虑\(dp\)

由于不同的键的效果是互不相同的,不存在一个键被代替的情况

\(dp_i\)表示从最少需要按\(i\)个键转移到最少需要按\(i-1\)个键的期望按键数量

\[dp_i=\frac{i}{n}+\frac{n-i}{n} (dp_{i+1}+1+dp_i) \]

解释一下,\(\frac{i}{n}\)表示凑巧按对了的期望,\(\frac{n-i}{n}\) 是按错的概率,既然按错了就需要按回来,需要按\(dp_{i+1}\)次,这次按了\(1\)键,需要加\(1\),注意,\(dp_i\)表示从最少需要按\(i\)个键转移到最少需要按\(i-1\)个键的期望按键数量,那么我们还需要转移到最少需要按\(i-1\)个键,需要按\(dp_i\)

推一下式子,得出递推式:

\[dp_i=\frac{n}{i}+\frac{n-i}{i}dp_{i+1} \]

显然,我们要从\(cnt\)开始转移,但是\(dp_{cnt}\)是啥啊?

要求出\(dp_{cnt}\),我们还需要前面的值,什么时候值是可知的呢?

可以从\(dp_n=1\)开始转移(需要按\(n\)个键,瞎按也\(100\%\)按到,我的代码里用了这个)

也可以从\(dp_{n+1}=0\)开始转移(感性理解,顶多按\(n\)键,不用按就少一键)

随便挑吧!

这里有个奇怪的问题,明明只用按\(cnt\)键,我们为什么处理到了\(dp_n\)呢,它的意义何在?

其实\(dp_n\)是有意义的,因为本来只需要按\(cnt\)次,但是被\(B\)君随机瞎按按坏啦,以至于退化到需要按\(n\)

现在就没有什么疑问了吧?

\(C++ Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#define N 100005
#define p 100003
#define ll long long
using namespace std;
int n,k,cnt;
ll inv[N],dp[N];
bool close[N];
vector<int>g[N];
int main()
{
    scanf("%d%d",&n,&k);
    inv[1]=1;
    for (int i=2;i<=n;i++)
        inv[i]=(ll)(p-p/i)*inv[p%i]%p;
    for (int i=1;i<=n;i++)
        for (int j=i;j<=n/i;j++)
            if (j==i)
                g[i*j].push_back(i); else
                {
                    g[i*j].push_back(i);
                    g[i*j].push_back(j);
                }
    for (int i=1;i<=n;i++)
        scanf("%d",&close[i]);
    for (int i=n;i>=1;i--)
        if (close[i])
        {
            for (vector<int>::iterator it=g[i].begin();it!=g[i].end();++it)
                close[(*it)]=!close[(*it)];
            cnt++;
        }
    ll ans=k;
    if (cnt<=k)
        ans=cnt; else
        {
            dp[n]=1;
            for (int i=n-1;i>k;i--)
                dp[i]=(ll)n*inv[i]%p+(n-i)*inv[i]%p*dp[i+1]%p;
            for (int i=k+1;i<=cnt;i++)
                ans=(ans+dp[i])%p;
        }
    for (int i=1;i<=n;i++)
        ans=ans*(ll)i%p;
    ans=(ans%p+p)%p;
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-08-07 20:46  GK0328  阅读(75)  评论(0编辑  收藏  举报