【NOIP模拟】table(动态规划)

题目背景

SOURCE:NOIP2016-RZZ-2 T2

题目描述

给定一个 n×m 的矩阵,行列均从 1 开始标号。

一个矩阵被认为是稳定的,当且仅当对于任意的 2≤i≤n,第 i 行的数的和不小于第 i−1 行的数的和,且最后一行的数的和小于等于 m ,并且要求矩阵中所有的元素都是非负的。

求所有 n×m 的稳定矩阵的方案数,答案对 109 取模。

输入格式

第一行一个整数 T ,表示数据组数。
每组数据一行两个整数 n,m 。

输出格式

输出 T 行,每行一个整数,表示方案数。

样例数据 1

输入


1 1 
2 2 
2 3

输出


25 
273

备注

【数据规模与约定】

对于 30% 的数据,n,m≤3。
对于 60% 的数据,n,m≤50。
对于 100% 的数据,1≤n,m≤2000;1≤T≤10。

【题目分析】

  题目意思显而易见,求两次dp:

  第一次,求出$C(i, j)$:

    对于这一次dp,其实就是求组合数,可以使用递推公式: $$C(i, j) = C(i - 1, j - 1) + C(i - 1, j)$$

    那么我们在求将$i$个数和为$j$的方案数时, 实际就是将$j$个1分成$i$份,可以看作从$j + i - 1$个数中选出$i - 1$个数(作为栅栏将1隔开)

    其实就是求$C(j + i - 1, i - 1)$

  第二次,求出$dp(i, j)$表示第$i$行和小于等于$j$的方案数。

    这一次dp采用前缀和累加记录。对于当前$dp(i, j)$, 先加上$dp(i, j - 1)$(累加,这样算出来的才是小于等于j的方案数),

    然后再加上$C(j + m - 1,m - 1) × dp(i - 1, j)$(上一行的和小于等于当前行的和, 则当前行的和为$j$时(方案数$C(j + m - 1,m - 1)$) ×上一行的和小于等于$j$(方案数$dp(i - 1, j)$).

    不要忘记取模(乘法爆int 先乘上 1LL )。

 

 

【code】

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;

const int N = 2005, Mod = 1e9;
int n, m, T;
int cnt[N<<1][N<<1], s[N][N];

inline void init(){
    for(int i = 0; i <= 4000; i++) cnt[i][0] = cnt[i][i] = 1;
    for(int i = 2; i <= 4000; i++)
        for(int j = 1; j < i; j++)
            cnt[i][j] = (cnt[i - 1][j - 1] + cnt[i - 1][j]) % Mod;
}

int main(){
    init();
//    cout<<sum[2][2]<<" "<<sum[2][1]<<" "<<sum[2][0];return 0;
    cin>>T;
    while(T--){
        cin>>n>>m;
        memset(s, 0, sizeof s);
        for(int i = 0; i <= m; i++) s[0][i] = 1;
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= m; j++){
                long long tmp = s[i - 1][j];
                tmp = (1LL * tmp * cnt[j + m - 1][m - 1]) % Mod;
                if(j) s[i][j] = (tmp + s[i][j - 1]) % Mod;
                else s[i][j] = tmp;
            }
        }
        cout<<s[n][m]<<endl;
    }
    return 0;
}
View Code
posted @ 2017-07-20 17:04  CzYoL  阅读(166)  评论(0编辑  收藏  举报