zoukankan      html  css  js  c++  java
  • Memory and Scores

    Memory and Scores

    题目链接:http://codeforces.com/contest/712/problem/D

    dp

    因为每轮Memory和Lexa能取的都在[-k,k],也就是说每轮两人分数的变化量在[-2k,2k];

    故可以定义状态:dp[times][diff]为第times次Memory和Lexa的分数差为diff的方案数.

    而dp[times][diff]可以从dp[times-1][diff-2k]到dp[times-1][diff+2k]转移而来;

    又因为变化量为-2k时的方案数为1(-k,k),

    变化量为-2k+1时的方案数为2(-k,k-1;-k+1,k),

    变化量为-2k+2时的方案数为3(-k,k-2;-k+1,k-1;-k+2,k),

    ...,

    变化量为-2k+m时的方案数为m+1,

    ...,

    变化量为0时的方案数为2k+1,

    ...,

    变化量为2k-m时的方案数为m+1,

    ...,

    变化量为2k-1时的方案数为2,

    变化量为2k时的方案数为1.

    所以状态转移方程为:dp[times][diff]=dp[times-1][diff-2k]+2*dp[times-1][diff-2k+1]+3*dp[times-1][diff-2k+2]+...+(m+1)*dp[times-1][diff-2k+m]+...+2*dp[times-1][diff+2k-1]+dp[times-1][diff+2k];

    这样的话,时间复杂度为O(k2t2),代码如下:

     1 #include<iostream>
     2 #include<cmath>
     3 #define M 1000000007LL
     4 #define TIME 105
     5 #define DIFF 300000
     6 #define BASE 150000
     7 using namespace std;
     8 typedef long long LL;
     9 LL a,b,k,t,ans;
    10 LL dp[TIME][DIFF];
    11 int main(void){
    12     cin>>a>>b>>k>>t;
    13     dp[0][a-b+BASE]=1;
    14     LL upper=a-b+BASE+2*k*t;
    15     LL lower=a-b+BASE-2*k*t;
    16     for(LL times=1;times<=t;++times){
    17         for(LL diff=lower;diff<=upper;diff++){
    18             for(LL m=0;m<=2*k;m++){
    19                 LL add=-2*k+m;
    20                 if(diff+add>=lower){
    21                     if(add)dp[times][diff]+=(dp[times-1][diff+add]+dp[times-1][diff-add])*(m+1);
    22                     else dp[times][diff]+=dp[times-1][diff]*(m+1);
    23                     dp[times][diff]%=M;
    24                 }
    25             }
    26         }
    27     }
    28     for(int i=BASE+1;i<=upper;++i)
    29         ans=(ans+dp[t][i])%M;
    30     cout<<ans<<endl;
    31 }
    View Code

    很显然,这会T,所以必须做出优化。

    注意到:

    dp[times][diff]是在dp[times][diff-1]的基础上前半段各个项减一,后半段各个项加一得到的,所以可以维护一个前缀和数组pre[i],那么

    dp[times][diff]=dp[times][diff-1]+(pre[diff+2k]-pre[diff-1])-(pre[diff-1]-pre[(diff-1)-2k-1])

    可以在O(1)的时间内完成,优化后的代码时间复杂度为O(kt2),代码如下:

     1 #include<iostream>
     2 #include<cmath>
     3 #define M 1000000007LL
     4 #define TIME 105
     5 #define DIFF 500000
     6 #define BASE 250000
     7 using namespace std;
     8 typedef long long LL;
     9 LL a,b,k,t,ans;
    10 LL dp[TIME][DIFF];
    11 LL pre[DIFF];
    12 int main(void){
    13     cin>>a>>b>>k>>t;
    14     dp[0][a-b+BASE]=1;
    15     LL upper=a-b+BASE+2*k*t;
    16     LL lower=a-b+BASE-2*k*t;
    17     for(LL times=1;times<=t;++times){
    18         for(LL diff=lower;diff<=upper;diff++)
    19             pre[diff]=pre[diff-1]+dp[times-1][diff],pre[diff]%=M;
    20         for(LL m=0;m<=2*k;m++){
    21             LL add=-2*k+m;
    22             if(add)dp[times][lower]
    23                 +=(dp[times-1][lower+add]+dp[times-1][lower-add])*(m+1);
    24             else dp[times][lower]+=dp[times-1][lower]*(m+1);
    25             dp[times][lower]%=M;
    26         }
    27         for(LL diff=lower+1;diff<=upper;diff++){
    28             dp[times][diff]=dp[times][diff-1]
    29                 +(pre[min(upper,diff+2*k)]-pre[diff-1])
    30                 -(pre[diff-1]-pre[max(lower,diff-1-2*k)-1]);
    31             dp[times][diff]=(dp[times][diff]+M)%M;
    32             //记得+M,减法模运算可能会出现负数
    33         }
    34     }
    35     for(int i=BASE+1;i<=upper;++i)
    36         ans=(ans+dp[t][i])%M;
    37     cout<<ans<<endl;
    38 }

    这样的代码仍然可以优化:

    1.可以用滚动数组来优化空间复杂度,从O(kt2)降低到O(kt),太懒没写╮(╯▽╰)╭;

    2.可以用快速傅里叶变换FFT优化时间复杂度,从O(kt2)继续降到O(kt lg(kt)),没学还不会写╮(╯▽╰)╭

    //昨天去面试微软俱乐部被嘲讽=。= 定个目标吧,这学期div2稳定4题怎么样?

  • 相关阅读:
    300+值得收藏的设计师免费资源站
    Apache 隐藏入口文件 index.php
    Nginx 虚拟主机下支持Pathinfo并隐藏入口文件的完整配置
    Java多线程
    Java注解
    Java异常机制
    面向对象
    数组
    Java方法(函数)
    Java流程控制(Scanner)
  • 原文地址:https://www.cnblogs.com/barrier/p/5875456.html
Copyright © 2011-2022 走看看