Algoogle

Algorithm for Programming Contest

AOJ 2595 Cookie Counter

Category: AOJ Tag: dp, modulo

Cookie Counter

問題概要


D(<=1,000,000,000,000)日でN(<=2,000)個のクッキーを食べたい.
ただし1日に食べる枚数はX(<=2,000)枚以下でなければならない.
食べ方は何通りあるか.

解法


実際に1枚以上クッキーを食べる日数はN日以下.
つまり1枚も食べない日を除いてDPすればよい.
その結果に対して食べる日と食べない日の組合せを求めれば答えになる.
dp[i][j] := i日でj枚食べる場合の数
とすれば

これは連続する区間の和になっているのでDPの更新の時に区間をスライドさせて行けば各日数の更新はO(N)になる.
これでこのDPの計算量はO(N^2)である.

あとは食べる日と食べない日の組合せを計算してやればよい.
各組合せの計算はDが大きいのでO(N)で計算する.

コード


(2595.cpp) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <bits/stdc++.h>
using namespace std;

const int mod = 1e9+7;

int n, x, dp[2048][2048], inv[2048];
long long d;

int solve()
{
        memset(dp,0,sizeof(dp));
        dp[0][0] = 1;
        for (int i = 0; i < n; i++) {
                for (int j = i; j < n; j++) {
                        dp[i+1][j+1] = (dp[i+1][j]+dp[i][j])%mod;
                        if(j+1 >= x) dp[i+1][j+1] = (dp[i+1][j+1]-dp[i][j-x+1]+mod)%mod;
                }
        }

        int ans = 0;
        for (int i = 1; i <= min(1LL*n, d); i++) {
                int tmp = dp[i][n];
                for (int j = 0; j < i; j++) {
                        tmp = 1LL*(d-j)%mod*tmp%mod;
                        tmp = 1LL*tmp*inv[i-j]%mod;
                }
                ans = (ans+tmp)%mod;
        }
        return ans;
}

bool input()
{
        scanf("%d%lld%d", &n, &d, &x);
        return n or d or x;
}

int main()
{
        inv[1] = 1;
        for(int i = 2; i < 2048; ++i) inv[i] = 1LL*inv[mod%i]*(mod-mod/i)%mod;

        while(input()) printf("%d\n", solve());
        return 0;
}

Comments