【BZOJ4872】分手是祝愿(动态规划,数学期望)

232 篇文章 0 订阅
94 篇文章 0 订阅

题面

BZOJ

题解

对于一个状态,如何求解当前的最短步数?
从大到小枚举,每次把最大的没有关掉的灯关掉
暴力枚举因数关就好

假设我们知道了当前至少要关 tot t o t
如果一个灯被动两次以上是没有任何意义的
所以,相当于,要动的灯只有 tot t o t
其他的是没有任何意义的
所以,题面可以变为:
现在有 tot t o t 1 1 ntot 0 0
每次随机选择一个数将其异或1
求最终变为 0 0 的期望

我们现在考虑一下
f[x]为剩下 x x 1的期望
并且我们知道了所有的值,
那么,我们不难推出:

f[x]=xn(f[x1]+1)+nxn(f[x+1]+1) f [ x ] = x n ( f [ x − 1 ] + 1 ) + n − x n ( f [ x + 1 ] + 1 )

也就是
f[x]=xnf[x1]+nxnf[x+1]+1 f [ x ] = x n f [ x − 1 ] + n − x n f [ x + 1 ] + 1

同时,我们有边界:
f[x]=x(xK) f [ x ] = x ( x ≤ K )
f[n]=f[n1]+1 f [ n ] = f [ n − 1 ] + 1
如果考虑把 f[n] f [ n ] 带入到 f[n1] f [ n − 1 ] 的式子中
我们可以得到只有 f[n1],f[n2] f [ n − 1 ] , f [ n − 2 ] 之间的关系式
如此递推下去就可以推出 f[K+1] f [ K + 1 ] f[K] f [ K ] 的关系式
这样就是常数项了
回朔带回去就可以求解
时间复杂度 O(nlogn) O ( n l o g n )

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 120000
#define MOD 100003
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int fpow(int a,int b)
{
    int s=1;
    while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
    return s;
}
int tot,n,K;
int a[MAX],ans[MAX];
int inv[MAX];
int DFS(int x,int ss)
{
    if(x<=K)return ans[x]=x;
    ss=(1ll*n*inv[x]%MOD+1ll*ss*(n-x)%MOD*inv[x]%MOD)%MOD;
    return ans[x]=(DFS(x-1,ss)+ss)%MOD;
}
int main()
{
    n=read();K=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=n;i;--i)
        if(a[i])
        {
            for(int j=1;j*j<=i;++j)
                if(i%j==0)
                {
                    a[j]^=1;
                    if(j*j!=i)a[i/j]^=1;
                }
            ++tot;
        }
    if(tot<=K)
    {
        for(int i=1;i<=n;++i)tot=1ll*tot*i%MOD;
        printf("%d\n",tot);
        return 0;
    }
    for(int i=1;i<=n;++i)inv[i]=fpow(i,MOD-2);
    DFS(n,1);
    for(int i=1;i<=n;++i)ans[tot]=1ll*ans[tot]*i%MOD;
    printf("%d\n",ans[tot]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值