其他分享
首页 > 其他分享> > [六省联考2017]分手是祝愿

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

作者:互联网

题目

发现无论是什么时候,毕姥爷的概率题我就是不会做

首先先看到一个显然的性质,就是一个开关最多被操作一次,于是整个序列最多也就被操作\(n\)次

看到有\(50\)分\(k=n\),于是只需要求一下最少几步关掉全部的灯即可

这里需要一个贪心,显然我们需要先去关编号大的灯,编号大的灯只会影响它的约数不会影响编号更大的灯,这样贪心可以保证每个开关只会被操作一次

调和级数存一下约数,暴力一下就做完了,这样就有\(80\)分了

我们考虑我们随机操作有什么用处

假设我们求出来原来最优需要\(x\)步就能全部关掉,那么我们随机操作到的可能恰好是我们需要关掉的,操作之后就只需要\(x-1\)步了,如果操作到的是一个原来不需要关掉的,我们就必须额外操作一次,就需要\(x+1\)步了

所以实际上这个问题的具体的开关状态并没有什么关系,只和当前最有策略需要多少步有关系

设\(dp_i\)表示到\(i\)步这个状态期望经过多少次

显然有

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

初值\(dp_x=1\)

很好理解,\(dp_{i+1}\)这个状态有\(\frac{i+1}{n}\)的概率选择到一个需要操作的灯转移到\(dp_i\),\(dp_{i-1}\)同理

可以直接高斯消元,但是复杂度\(O(n^3)\)显然不科学

于是我就不会了,本以为这个柿子有奇妙的性质,但是无论怎么化都发现这是一个需要高斯消元的垃圾柿子

于是抄题解

换个角度考虑,设\(f_i\)表示从\(i\)这个状态转移到\(i-1\)的期望次数

有\(\frac{i}{n}\)的概率直接转移过去,这里的消耗是\(\frac{i}{n}\)

有\(\frac{n-i}{n}\)的概率转移到\(i+1\),这里的消耗是\(\frac{n-i}{n}\),之后得从\(i+1\)转移到\(i-1\),需要自然是\(f_{i+1}+f_{i}\)

于是

\[f_i=1+\frac{(n-i)(f_i+f_{i+1})}{n}\]

随便化一化,发现

\[f_i=\frac{f_{i+1}\times n}{i}-f_{i+1}\]

可以直接递推了

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 100005
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const LL mod=100003;
inline int read() {
    int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
void exgcd(LL a,LL b,LL &x,LL &y) {if(!b) {x=1,y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
inline LL inv(LL a) {LL x,y;exgcd(a,mod,x,y);return (x%mod+mod)%mod;}
LL fac;
int a[maxn],n,m;
int now;
std::vector<int> v[maxn];
LL dp[maxn];
int main() {
    n=read(),m=read();
    fac=1;for(re int i=1;i<=n;i++) fac=(fac*i)%mod;
    for(re int i=1;i<=n;i++)
        for(re int j=i;j<=n;j+=i) v[j].push_back(i);
    for(re int i=1;i<=n;i++) a[i]=read();
    for(re int i=n;i;--i) {
        if(!a[i]) continue;
        now++;
        for(re int j=0;j<v[i].size();j++) a[v[i][j]]^=1;
    }
    if(now<=m) {printf("%lld\n",(LL)now*fac%mod);return 0;}
    dp[n]=1;
    for(re int i=n-1;i>=1;--i) dp[i]=1ll+(dp[i+1]+1ll)*n%mod*inv(i)%mod-(dp[i+1]+1ll),dp[i]=(dp[i]+mod)%mod;
    LL ans=m;
    for(re int i=now;i>m;--i) ans=(ans+dp[i])%mod;
    printf("%lld\n",ans*fac%mod);
    return 0;
}

标签:frac,define,操作,六省,2017,include,联考,dp,mod
来源: https://www.cnblogs.com/asuldb/p/10409063.html