Good Bye 2017 D.New Year and Arbitrary Arrangement - 数学

题目大意:你有一个随机数生成器,有p的概率生成字符a,(1-p)的概率生成b。给定p=u/v,k,当生成的字符串中有至少k个形如’ab’的子序列(注意不是子串,例如abaab有4个形如’ab’的子序列)的时候就停止。不难注意到,停止的时候显然可能有多于k个形如’ab’的子序列。现在求期望情况下停止的时候有多少个形如’ab’的子序列。
k<=1000。答案对大质数取模输出。

题解:显然考虑dp……
如果令dp[i][j][t]表示前i个字符有j个a现在’ab’子序列的数量(下文记作序列数量)达到了t的概率。显然是能做的,但是i和t都是无穷大……
不难发现这样一个性质:如果加入一个a,序列数量不变;否则序列数量增加量等价于之前a的数量,显然。
因此你只要纪录a的数量即可,即令dp[i][j]表示当前有i个a,序列数量是j的概率。其中k>j。
转移是显然的,转移过程中如果转移到了j>=k的部分就统计入答案。
这样就完了么?显然没有,因为还有一种情况,就是积攒了很多个a,也就是i>=k,但是现在k>j,这样你再放1个b,序列数量就>=k了,但是在dp过程中没有统计入答案(注意,k>i,并且i+j>=k的时候会在dp过程中直接计入答案)。

这样,不妨考虑dp[i][j]在i>=k的值,不难发现这时候有:(记p=pa/(pa+pb),q=1-p)

dp[i][j]=pdp[i1][j]=...=pik+1dp[k][j]

这样,这部分对答案的贡献就是:
j=0k1i=k+oodp[k1][j]pik+1q(i+j)

j=0k1qdp[k1][j]i=k+oopik+1(i+j)

对后面那个数列求和,可以得到上式为:
j=0k1qdp[i1][j][p(k+j)1p+p2(1p)2]

注意到q=1-p,因此有:
Answer+=j=0k1pdp[i1][j](k+j+pq)

然后……就做完了(注意这时dp[0][0]=1/p事后的情况)。
一开始我是直接dp[0][0]=1.这样会发现dp[0][0]可以转移到dp[0][0],我当时没有处理,导致事后统计答案的事后需要乘上1/p。(如果你令dp[0][0]=1的话以为着什么都不放,也就是这样做出来的答案第一个字符都是a,但是你也可以放若干个b,产生 +ooi=0qi=11q=1p 的概率贡献)。但事实上dp[0][0]应该设为1/p最合适,即考虑先放若干p的情况。
然后……转移时dp[i][j]=p*dp[i-1][j]+q*dp[i][j-i]。
其实写成dp[i+1][j]+=p*dp[i][j],dp[i][i+j]+=q*dp[i][j],这样i+j>=k的事后直接ans+=dp[i][j] q(i+j)即可。
然后……粘代码(写丑了成了O(n^2lgn))。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 1010
#define mod 1000000007
#define inv(x) fast_pow(x,mod-2)
#define lint long long
using namespace std;
int dp[N][N];
int fast_pow(int x,int k)
{
    if(k==0) return 1;
    if(k==1) return x%mod;
    int ans=fast_pow(x,k>>1);
    ans=(lint)ans*ans%mod;
    if(k&1) ans=(lint)ans*x%mod;
    return ans;
}
int main()
{
    int k,a,b;scanf("%d%d%d",&k,&a,&b);
    int p=(lint)a*inv(a+b)%mod,q=(lint)b*inv(a+b)%mod;
    dp[0][0]=1;int ans=0;
    for(int i=0,x;i<k;i++)
        for(int j=0;j<=k;j++)
            if(dp[i][j])
            {
                x=dp[i][j],(dp[i+1][j]+=(lint)p*x%mod)%=mod;
                if(i+j<k) (dp[i][i+j]+=(lint)q*x%mod)%=mod;
                else (ans+=(lint)inv(p)*q%mod*x%mod*(i+j)%mod)%=mod;
            }
    dp[0][0]=1;int x=((lint)p*inv(q)%mod+k)%mod;
    for(int i=0;i<k;i++)
        (ans+=(lint)dp[k-1][i]%mod*((i+x)%mod)%mod)%=mod;
    printf("%d\n",ans);return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值