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"); } }
相关文章推荐
- BZOJ 1833 count 数字计数 数位DP
- 【BZOJ 1833】【ZJOI 2010】[数位DP]count 数字计数
- bzoj 1833: [ZJOI2010]count 数字计数【数位dp】
- [Lucas+数位DP] 2015 计蒜之道 复赛 C. 360的产品试用体验
- [省选前题目整理][BZOJ 1833][ZJOI 2010]count 数字计数(数位DP)
- 【bzoj1833】[ZJOI2010]count 数字计数 数位DP
- Manthan, Codefest 17 E. Salazar Slytherin's Locket(数位DP)(进制)
- 2017.10.20(积性函数,BSGS,K进制数位DP)
- [BZOJ1833][ZJOI2010]count 数字计数(数位dp)
- UVA 10712 - Count the Numbers (数位DP)
- BZOJ 1833: [ZJOI2010]count 数字计数 【数位DP】
- Manthan, Codefest 17 E. Salazar Slytherin's Locket(数位DP)(进制)
- SGU 390 Tickets (数位dp,k进制树的合并)
- Bzoj1833:[ZJOI2010]count 数字计数:数位dp
- [BZOJ 1833] [ZJOI2010] count 数字计数 【数位DP】
- Manthan, Codefest 17 E. Salazar Slytherin's Locket(数位DP)(进制)
- HDU 4588 Count The Carries 数位DP || 打表找规律
- Manthan, Codefest 17 E. Salazar Slytherin's Locket(数位DP)(进制)
- [BZOJ 1833] count 数字计数 数位DP(附数位DP总结)
- 【BZOJ1833】【数位DP】 count 数字计数