您的位置:首页 > 其它

ccount 进制拆分(Lucas 数位dp)

2017-10-20 19:21 351 查看

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
, down
, utot, dtot;
int comb[P][P];
LL _dp
[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");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: