ccount
10.20
思路:
對於一個C(n,m) 我們要求的就是它%5後是否爲0。
這個nm太大了,我們沒有辦法直接計算,又發現這個模數P=5是個質數。
考慮Lucas,分解之後就會成爲C(a1,b1) * C(a2,b2) * … * C(ai,bi)。
0 <= ai,bi <= 4
要讓C(a1,b1) * C(a2,b2) * … * C(ai,bi) = 0 (mod 5),只要讓其中任意一個C = 0 (mod 5)就好了。
什麼時候能滿足條件呢?就是有至少一個(bi > ai)。
也就是說我們對於未拆分的每一個C(n, i)的n和i進行5進制分解。
但是我們發現要處理的是[l,r]一個區間,也就是說我們要處理的是一堆數。
但是這些數是連續的,而且ans滿足可減性,所以就是求C(n,1)~C(n,r)的ans - C(n,1)~C(n,l-1)的ans。
那麼問題就轉換爲求有多少個小於x,且每一位爲0~4,在至少一位上比n的五進制分解大。
自然就是數位dp啦!!!
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
const int N = 110;
const int P = 5;
int up[N], down[N], utot, dtot;
int comb[P][P];
LL _dp[N][5];
void init() {
for(int i = 0; i < P; i++)
for(int j = 0; j <= i; j++) {
if(j == 0 || j == i)
comb[i][j] = 1;
else
comb[i][j] = (comb[i-1][j-1] + comb[i-1][j]) % P;
}
}
void divi(LL n, int *arr, int &tot) {
if(n == 0) {
tot = 1;
arr[1] = 0;
} else {
tot = 0;
while(n) {
arr[++tot] = int(n % P);
n /= P;
}
}
}
LL dp(int i, int r, bool flag) {//r是當前狀態下%5的答案(其實記錄是0或非0就行啦)
if(_dp[i][r] != -1 && flag == false) return _dp[i][r];
if(i == 0 ) return r == 0;
LL rt = 0;
int top = flag ? up[i] : P - 1;
for(int v = 0; v <= top; v++)
rt += dp(i - 1, r * comb[down[i]][v] % P, flag && v == top);//模擬公式
if(!flag) return _dp[i][r] = rt;
else return rt;
}
LL solve(LL n) {
divi(n, up, utot);
return dp(utot, 1, true);
}
int main() {
freopen("ccount.in","r",stdin);
freopen("ccount.out","w",stdout);
int T; scanf("%d", &T);
init();
while(T--) {
LL l, r, n;
scanf("%I64d%I64d%I64d", &l, &r, &n);
divi(n, down, dtot);
memset(_dp, -1, sizeof(_dp));
LL ans = solve(r);
if(l != 0)
ans = ans - solve(l - 1);
printf("%I64d\n", ans);
}
return 0;
}
當然dp還可以優化一下,直接預處理出從某一位開始(有最高位限制的情況之下)的方案數,然後處理到一個0的時候就可以直接返回啦。(代碼肯定就要醜一點咯)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
LL l, r, n, a[100], k, ans, b[100], tot, Sum[100];
LL ipow(LL a, LL b){
LL rt = 1;
for( ; b; b>>=1, a=a*a)
if(b & 1) rt = rt * a;
return rt;
}
LL dfs(LL step, bool flag){
if( !step ) return 0LL;
LL rt = 0;
if( flag ){
if(b[step] <= a[step]){
rt += b[step] * dfs(step-1, 0);
rt += dfs(step-1, 1);
}
else{
rt += ((b[step] - a[step] - 1) * ipow(5LL, step-1));//此位C=0
rt += Sum[step-1];
rt += (a[step] + 1) * dfs(step-1, 0);
}
}
else{
rt += (a[step] + 1) * dfs(step-1, 0);
rt += (4 - a[step]) * (ipow(5LL, step-1));//此位C=0
}
return rt;
}
LL calc(LL x){
tot = 0;
for(LL i=0; i<100; i++) b[i] = 0, Sum[i] = 0;
LL p = x , rt = 0;
while( p ){
b[++tot] = p % 5;
p /= 5;
}
Sum[0] = 1;
for(LL i=1; i<=tot; i++)
Sum[i] = Sum[i-1] + b[i] * ipow(5LL, i-1);
rt = dfs(k, 1);
return rt;
}
int main(){
freopen("ccount.in", "r", stdin);
freopen("ccount.out", "w", stdout);
int T; scanf("%d", &T);
while ( T-- ){
scanf("%I64d%I64d%I64d", &l, &r, &n);
LL p = n ; k = 0 ;
while( p ){
a[++k] = p % 5;
p /= 5;
}
printf("%I64d", calc( r ) - calc( l - 1 ));
printf("\n");
}
}