codeforces 233 D. Table(思维+dp )

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

题意:问在n*m的矩阵中满足在每一个n*n的矩阵里画k个点,一共有几种画法。

 

题解:其实这题挺简单的但是有一个优化要注意一下,接下来将一下这题的解法。

拿一个连续长度为n的块。1,2将其分成3部分第一部分为第一列a,第二部分为中间重合的部分,第三部分为最后一列c设Si表示,第i列一共有几个点。

显然Sa=Sc于是乎便有思路了吧,所有可能性就是1~n的C(n,k)^cnt(cnt是m/n or m/n + 1)然后用dp[i][j]来存第i个有j个点的一共有几种,然后

转移一下就行具体怎么转移法看代码。

#include <iostream>
#include <cstring>
#include <cstdio>
#define mod 1000000007
using namespace std;
typedef long long ll;
ll c[200][200] , dp[110][10010];
//这里要用到快快速幂毕竟最多有1e18次。
ll modexp(ll a , ll b) {
    ll ret = 1;
    ll tmp = a;
    while(b) {
        if(b & 0x1) ret = ret * tmp % mod;
        tmp = tmp * tmp % mod;
        b >>= 1;
    }
    return ret;
}
int main() {
    ll n , m , k;
    scanf("%lld%lld%lld" , &n , &m , &k);
    for(int i = 1 ; i <= 100 ; i++) c[i][1] = i , c[i][i] = 1 , c[i][0] = 1;
    for(int i = 2 ; i <= 100 ; i++) {
        for(int j = 2 ; j < i ; j++) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
    }//预处理组合数
    memset(dp , 0 , sizeof(dp));
    ll cnt = m / n;
    ll gb;
    if(m % n) {
        gb = m - n * cnt;
        cnt++;
    }
    else gb = n;
    //gb在这里起指示作用,当整除的时候一共有cnt-1对,不能整除的话有gb个是cnt对的剩下cnt-1对。
    for(int i = 0 ; i <= n ; i++) dp[i][0] = 1;
    for(int i = 1 ; i <= n ; i++) {
        //注意这个优化求次方一定要在外层不然会超时。
        for(int j = 0 ; j <= n && j <= k ; j++) {
            ll gg = c[n][j];
            ll gl;
            if((ll)i <= gb) gl = modexp(gg , cnt);
            else gl = modexp(gg , cnt - 1);
            for(int l = j ; l <= k ; l++) {
                if(l == 0) continue;//细节稍微注意一下
                dp[i][l] += dp[i - 1][l - j] * gl;
                dp[i][l] %= mod;
            }
        }
    }
    printf("%lld\n" , (dp[n][k] + mod) % mod);
    return 0;
}
posted @ 2017-06-09 20:00  Gealo  阅读(310)  评论(0编辑  收藏  举报