【题解】CF712D Memory and Scores(DP)

Description

\(AB\)两人玩一个游戏,两人玩 \(t\)

每人每次随机且等概率从 \([-k,k]\) 中取一个数字加到总得分中 得分高者赢

已知\(A,B\)初始分别有 \(a,b\) 分,问A取得胜利的方案数是多少

答案 \(\mod 1000000007\)

\(a,b,t \leqslant 100 ,k \leqslant 1000\)

Solution

直接考虑这题我们可以得到一个特别恶心的状态和一个转移:

\(f[i][j][k]\) 表示前 \(i\)\(A\) 的分数是 \(j\),\(B\) 的分数是 \(k\)的方案数。

我们转移发现是 \(100*(100*1000)*(100*1000)*1000*1000\)(至少我只会这个复杂度的转移

这,谁顶的住。。。

我们优化一下这个状态我们发现其实这两个人的决策分别是什么对方案数的影响可以最后计算

换句话说就是这两个人的决策是独立的,我们可以分别考虑两个人的决策方案数,然后把他们乘起来。

那么状态变成了:

\(f1[i][j],f2[i][j]\) 表示分别是 \(A\)\(B\) 的分数是 \(j\) 的方案数。那么我们每次转移只要枚举一下,当前对于 \(A\),\(B\) 选什么。

最后合并起来就好了。

复杂度 \(100*(1000*100)*1000\) (好像可以过的样子

先放一个暴力代码:

#include <bits/stdc++.h>
using namespace std;

const int N=105,M=N*1000*2,P=1000000007;
int dp1[2][M],dp2[2][M],sum1[M],sum2[M];

int main(){
    int a=0,b=0,k=0,t=0;
    scanf("%d%d%d%d",&a,&b,&k,&t);
    a+=1e5; b+=1e5;
    dp1[0][a]=dp2[0][b]=1;
    for(int i=1;i<=t;++i){
        int now=i%2,pre=(i-1)%2;
        memset(dp1[now],0,sizeof(dp1[now]));
        memset(dp2[now],0,sizeof(dp2[now]));
        for(int j=-k*i;j<=k*i;++j){
        	int l=max(-k*(i-1),j-k),r=min(k*(i-1),j+k);
        	for(int p=l;p<=r;++p){
        		dp1[now][a+j]+=dp1[pre][a+p];
        		dp1[now][a+j]%=P;
        		dp2[now][b+j]+=dp2[pre][b+p];
        		dp2[now][b+j]%=P;
			}
        }
    }
    int ans=0;
    for(int i=a-k*t;i<=a+k*t;++i)
	   	for(int j=b-k*t;j<i;++j)
			ans+=(long long)dp1[t%2][i]*dp2[t%2][j]%P,ans%=P;
    ans=(ans%P+P)%P;
    printf("%d\n",ans);
    return 0;
}

过个头啊,我们还要优化。

我们再看看那个方程发现其实我们可以对那个枚举当前选什么然后从前一个转移哪里进行优化。(也就是p的操作)。

其实这一部分做的就是在求

\(\sum_{p=j-k}^{j+k} dp[i-1][a+p]\)

\(j\) 是当前我们枚举的那个分数增加值。

那么我们对这一部分求个前缀和就好了

这样我们把最重要的一个 \(1000\) 给干掉了

复杂度 \(100*(1000*100)\) (稳得一批。

我们得相信CF的评测机!!!

#include <bits/stdc++.h>
using namespace std;

const int N=105,M=N*1000*2,P=1000000007;
int dp1[2][M],dp2[2][M],sum1[M],sum2[M];

int main(){
    int a=0,b=0,k=0,t=0;
    scanf("%d%d%d%d",&a,&b,&k,&t);
    a+=1e5; b+=1e5;
    dp1[0][a]=dp2[0][b]=1;
    sum1[a]=sum2[b]=1;
    for(int i=1;i<=t;++i){
        int now=i%2;
        memset(dp1[now],0,sizeof(dp1[now]));
        memset(dp2[now],0,sizeof(dp2[now]));
        for(int j=-k*i;j<=k*i;++j){
            int l=max(-k*(i-1),j-k),r=min(k*(i-1),j+k);
            dp1[now][a+j]+=(sum1[r+a]-sum1[l+a-1])%P;
            dp1[now][a+j]%=P;
            dp2[now][b+j]+=(sum2[r+b]-sum2[l+b-1])%P;
            dp2[now][b+j]%=P;
        }
        for(int j=1;j<M;++j){
            sum1[j]=sum1[j-1]+dp1[now][j];
            sum1[j]%=P;
            sum2[j]=sum2[j-1]+dp2[now][j];
            sum2[j]%=P;
        }
    }
    int ans=0;
    for(int i=1;i<M;++i)
        ans+=(long long)sum2[i-1]*dp1[t%2][i]%P,ans%=P;
    ans=(ans%P+P)%P;
    printf("%d\n",ans);
    return 0;
}
posted @ 2019-07-24 21:21  章鱼那个哥  阅读(185)  评论(0编辑  收藏  举报