分手是住院「期望dp」

这个题如果各位大神做的话肯定是"当时秒切"

像我这种据若就算了吧

题解

首先考虑没有随机的情况

从大到小枚举看是最优的,

感性理解,你大的一定要选,你如果小的选了之后你大的可能让当前小的翻转,你当前选的可能是无意义的,

(说人话就是你选大的可能会对小的造成影响,你选小的一定不会对大的造成影响)

设f[i]表示当你还剩i个开关要按时期望步数

那么题目中说最后k步选最优策略就没用了,f[i]一定会选择最优即一定按需要按的必须选的灯f[i]=1

那么考虑随机,

贡献分两部分,1.按你需要按的$\frac{i}{n}$2.按不需按的是$\frac{n-i}{n}*(f[i+1]+f[i]+1)$

解释一下,你选到不该选的,你要用$1$步到$f[i+1]$状态,然后你要用$f[i+1]$步翻转回来,,回到当前状态之后你还要花$f[i]$步达到当前选到正确灯状态(这个式子让我们想起了tree这个题,很像不是吗)

关于求必选集合,用$vector$维护约数就行了

代码

#include
using namespace std;
#define ll long long
#define A 1010101
#define mod 100003
ll f[A],a[A],inv[A];
ll cnt=0,n,k;
vector  vec[A];
int main(){
    scanf("%lld%lld",&n,&k);
    f[n]=1;inv[1]=1;
    for(ll i=2;i<=n;i++) 
        inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(ll i=1,v;i<=n;i++)
        scanf("%lld",&a[i]);
    for(ll i=1;i<=n;i++)
        for(ll j=i;j<=n;j+=i)
            vec[j].push_back(i);
    for(ll i=n;i>=1;i--){
        if(!a[i]) continue ;
        for(ll j=0;j)
            a[vec[i][j]]^=1;
        cnt++;
    }
    for(ll i=n-1;i>k;i--)
        f[i]=(n+(n-i)*f[i+1]%mod)*inv[i]%mod;
    for(ll i=k;i>=1;i--)
        f[i]=1;
    ll ans=0;
    for(ll i=1;i<=cnt;i++)
        (ans+=f[i])%=mod;
    for(ll i=1;i<=n;i++)
        ans=ans*i%mod;
    printf("%lld\n",ans);
}
View Code

你可能感兴趣的:(分手是住院「期望dp」)