zoukankan      html  css  js  c++  java
  • [Codeforces712D] Memory and Scores(DP+前缀和优化)(不用单调队列)

    [Codeforces712D] Memory and Scores(DP+前缀和优化)(不用单调队列)

    题面

    两个人玩游戏,共进行t轮,每人每轮从[-k,k]中选出一个数字,将其加到自己的总分中。已知两人的初始得分分别为a和b,求第一个人最后获胜的方案数。两种方案被认为是不同的,当且仅当存在其中一轮,其中一人选到的数字不同。a, b, t≤100,k≤1000

    分析

    两个人的操作是独立的,设(dp1[i][j])表示第1个人玩i轮得到j分的方案数,第2个人同理

    则有(dp1[0][a]=0)

    (dp1[i][j]=sum _{u=j-k}^{j+k} dp1[i-1][u])

    第2个人只需要把dp的初始值改成(dp2[0][b]=0)即可。这样的空间复杂度是(O(kt^2)),时间复杂度是(O(k^2t^2)),考虑优化。

    首先可以用滚动数组,这样空间复杂度就变成了(O(kt))

    注意到(u in [j-k,j+k]),可以用前缀和优化,定义(sum1[i][j] = sum_{u=-maxv}^{maxv} sum1[i][u]),则dp方程可以改写成(dp1[i][j]=sum[i-1][j+k]-sum[i-1][j+k-1]).时间复杂度(O(kt^2)),对于dp2我们同理维护

    最后统计答案的时候只需要用第1个人得到i分的方案数乘上第2个人得到<i分的方案数即可,这时候又可以用上我们的sum数组

    [ans=sum_{i=-maxv}^{maxv} dp1[t][i] imes sum2[t][i-1] ]

    代码

    #include<iostream>
    #include<cstdio>
    #define maxv 200100 
    #define mod 1000000007
    using namespace std;
    int a,b,k,t;
    struct arr{
    	long long a[maxv*4+5];
    	inline long long& operator [] (const int index){
    		return a[index+maxv*2];
    	}
    };
    arr dp1[2],dp2[2]; 
    arr sum1[2],sum2[2];
    int main(){
    	int now1,now2;
    	scanf("%d %d %d %d",&a,&b,&k,&t);
    	dp1[0][a]=1;
    	for(int j=-maxv;j<=maxv;j++) sum1[0][j]=(sum1[0][j-1]+dp1[0][j])%mod;
    	now1=0;
    	for(int i=1;i<=t;i++){
    		now1^=1;
    		for(int j=-maxv;j<=maxv;j++){
    			dp1[now1][j]=sum1[now1^1][j+k]-sum1[now1^1][j-k-1];
    			dp1[now1][j]=(dp1[now1][j]+mod)%mod;
    		}
    		sum1[now1][-maxv-1]=0;
    		for(int j=-maxv;j<=maxv;j++){
    			sum1[now1][j]=sum1[now1][j-1]+dp1[now1][j];
    			sum1[now1][j]%=mod;
    		}
    	}
    	
    	dp2[0][b]=1;
    	for(int j=-maxv;j<=maxv;j++) sum2[0][j]=(sum2[0][j-1]+dp2[0][j])%mod;
    	now2=0;
    	for(int i=1;i<=t;i++){
    		now2^=1;
    		for(int j=-maxv;j<=maxv;j++){
    			dp2[now2][j]=sum2[now2^1][j+k]-sum2[now2^1][j-k-1];
    			dp2[now2][j]=(dp2[now2][j]+mod)%mod;
    		}
    		sum2[now2][-maxv-1]=0;
    		for(int j=-maxv;j<=maxv;j++){
    			sum2[now2][j]=sum2[now2][j-1]+dp2[now2][j];
    			sum2[now2][j]%=mod;
    		}
    	}
    	
    	long long ans=0;
    	for(int i=-k*t+a;i<=k*t+a;i++){
    		ans+=dp1[now1][i]*sum2[now2][i-1]%mod;
    		ans%=mod;
    	}
    	printf("%I64d
    ",ans);
    }
    
    
  • 相关阅读:
    P1886 滑动窗口 单调队列
    用三维的视角理解二维世界:完美解释meshgrid函数,三维曲面,等高线,看完你就懂了。...
    用三维的视角理解二维世界:完美解释meshgrid函数,三维曲面,等高线,看完你就懂了。...
    SaltStack入门
    编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    time模块中time.time与time.sleep
    【函数篇】装饰器
    【函数篇】函数的进阶,名称空间、作用域、函数的嵌套、作用域链
    闭包!!!
    默认参数的陷阱
  • 原文地址:https://www.cnblogs.com/birchtree/p/11241982.html
Copyright © 2011-2022 走看看