1. 程式人生 > >BZOJ4737: 組合數問題 數位DP+LUCAS定理

BZOJ4737: 組合數問題 數位DP+LUCAS定理

Descripition 如果給定n,m和k,對於所有的0≤i≤n,0≤j≤min(i,m)有多少對(i,j)滿足C(i,j)是k的倍數。

Sample Input 3 23 23333333 23333333 233333333 233333333 2333333333 2333333333

Sample Output 851883128 959557926 680723120

挺強的,這題。 我們想一下其實C(i,j)%k==0就OK嗎。 你就lucas一下式子就可以把n,m變成一個k進位制的數, n由a1,a2,a3,…,an,m由b1,b2,b3,…,bn構成。 C(n,m)就等於C(a1,b1) * C(a2,b2) * … * C(an,bn) 如果要滿足C(i,j)%k==0,就只要有一對a1 < b1即可, 根據這個進行數位DP即可

#include <cstdio>
#include <cstring>

using namespace std;
typedef long long LL;
const LL mod = 1000000007;

LL f[65][2][2][2][2];
int a[65], b[65], K;

LL dfs(int k, int op1, int op2, int op3, int op4) {
    if(k == 0) return op1;
    if(f[k][op1][op2][op3][op4] != -1) return f[k][op1][op2][op3][op4];
    int
limit1 = K - 1; if(!op2) limit1 = a[k]; int limit2 = K - 1; if(!op3) limit2 = b[k]; LL ans = 0; for(int i = 0; i <= limit1; i++) { for(int j = 0; j <= limit2; j++) { if(!op4 && j > i) break; int h1 = 1, h2 = 1, g = op1, kk = 0; if
(!op2 && i == limit1) h1 = 0; if(!op3 && j == limit2) h2 = 0; if(j > i) g = 1; if(op4 || j != i) kk = 1; (ans += dfs(k - 1, g, h1, h2, kk)) %= mod; } } f[k][op1][op2][op3][op4] = ans; return ans; } int main() { int T; scanf("%d%d", &T, &K); while(T--) { LL n, m; scanf("%lld%lld", &n, &m); if(m > n) m = n; int len1 = 0, len2 = 0; while(n) a[++len1] = n % K, n /= K; while(m) b[++len2] = m % K, m /= K; for(int i = len2 + 1; i <= len1; i++) b[i] = 0; memset(f, -1, sizeof(f)); printf("%lld\n", dfs(len1, 0, 0, 0, 0)); } return 0; }