【动态规划01】bzoj4872分手是祝愿(期望)

NOI将近,本蒟蒻学校名额水进了省队之后在家自学,然而年轻的我并没有意识到在家自学的学习效率近乎为0。
为了提高自己不FE的概率,所以在博客上每日更新一道dp题,达到督促自己的目的。
再加上本人是一个究极蒟蒻,所以题解自然会写的简单明了,连⑨都能明白(?)。

扯淡有点久,更新的第一道dp题就决定是省选考试的这道比较容易的期望Dp题目。

题目描述

Zeit und Raum trennen dich und mich.

时空将你我分开。

B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为从 1 到 n 的正整数。

每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,游戏的目标是使所有灯都灭掉。

但是当操作第 i 个开关时,所有编号为 i 的约数(包括 1i)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。

B 君发现这个游戏很难,于是想到了这样的一个策略,每次等概率随机操作一个开关,直到所有灯都灭掉。

这个策略需要的操作次数很多, B 君想到这样的一个优化。如果当前局面,可以通过操作小于等于 k 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 k 步)操作这些开关。

B 君想知道按照这个策略(也就是先随机操作,最后小于等于 k 步,使用操作次数最小的操作方法)的操作次数的期望。

这个期望可能很大,但是 B 君发现这个期望乘以 n 的阶乘一定是整数,所以他只需要知道这个整数对 100003 取模之后的结果。

输入输出格式

输入格式:

第一行两个整数 n, k。

接下来一行 n 个整数,每个整数是 0 或者 1,其中第 i 个整数表示第 i 个灯的初始情况。

输出格式:

输出一行,为操作次数的期望乘以 n 的阶乘对 100003 取模之后的结果。

这道题的数据范围是 对于 100% 的测试点, 1 ≤ n ≤ 100000; 0 ≤ k ≤ n。
首先我们思考这样一个问题,假如可以通过k次(或以下)操作将其全变为0,那么这个操作次数是一定的,首先同一个灯不可能进行两次操作(1^1^1==1),那么对于一组灯我们暴力从后往前进行枚举,如果这个灯是1那么必然要将其灭掉(选择更小的数并不能对其造成影响),我们可以在O(sqrt(n)*n)的时间复杂度内暴力得出次数值。

这道题的数据十分的水,如果你按照以上所写的暴力直接输出期望(概率100%*次数)可以得到80分,当然如果你和我一样将所有的灯变为1的话,你就会爆0(论D2得分如何从三位数变成个位数)。

那么对于需要k次以上操作才能到达的情况,很明显如果表示每次灯的状态这是不可能做到的。但事实上我们会发现对于每个状态,我们只需要记录他到达全灭状态的次数就可以了。
显然经过每次操作到达的下一个状态一定是i+1或者i-1。
设f[i]为状态为i时的期望,那么对于i<=k时,有f[i]=i。
对于i>k,f[i]=f[i-1] in +f[i+1] nin +1(1==(i/n)+((n-i)/n)
/*
由期望公式
E=Σp*(E1+X1)+Σp2*(E+X2)
其中E1为下一个状态,p1和X1分别为将当前状态转移到下一个状态的概率和花费,p2和X2分别为保持当前状态的概率和花费。
以上转自http://blog.csdn.net/qq_31759205/article/details/54730101
*/
已知边界条件f[n]=f[n-1]+1,因为如果需要最大步数的时候,无论取任何操作,一定都会使操作数减少。

然而这个递推式成立的条件是f[i-1]存在,那么当k=1/k=0的时候显然无法得出结论。
那么这时候要怎么办?那么就是将递推式中存在的f[i-1]给消掉。

设g[i]=f[i]-f[i-1],则有g[i+1]=f[i+1]-f[i]。
将f[i+1]用f[i]与f[i-1]表示
g[i+1]=f[i+1]-f[i]=(f[i]-1-f[i-1] in ) nni -f[i]
= (f[i]f[i1])inni
= g[i]inni
由此可以得出g[i]= g[i+1](ni)+ni
同时g[n]=f[n]-f[n-1]=1,故递推可求解。
故答案为f[cnt],而此时,由于f[k]=k已知,故f[n]可以表示为 cntk+1g[i] +k
综上为正解。

#include
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
const int maxn=100010;
const int INF=1e9+7;
const int mod=100003;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
    char ls;ll x=0,sng=1;
    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
    return x*sng;
}
/*----------------------------------------------------------------------------*/
ll n,k,light[maxn],g[maxn];
ll cnt=0,ans=0;
void change(int x)
{
    fer(i,1,sqrt(x))
        if(x%i==0)
        {
            light[i]^=1;
            if(i!=sqrt(x))light[x/i]^=1;
        }
    return ;
}
ll pw(ll x)
{
    ll y=mod-2,ans=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)ans=ans*x%mod;
    return ans;
}
int main()
{
    n=read();k=read();
    fer(i,1,n)light[i]=read();
    far(i,n,1)
        if(light[i]==1)
        {
            change(i);
            cnt++;
        }
    if(cnt<=k)
    {
        for(ll i=2;i<=n;i++)
        cnt=cnt*i%mod;
        cout<return 0;
    }
    g[n]=1;
    far(i,n-1,k+1)g[i]=(g[i+1]*(n-i)+n)*pw(i)%mod;//逆元
    ans=k;
    fer(i,k+1,cnt)ans+=g[i];
    for(ll i=2;i<=n;i++)
        ans=ans*i%mod;
    cout<return 0;
}

你可能感兴趣的:(动态规划(弱省省选~NOI))