思路
我们先用前缀和将问题转化为求范围内满足条件的数的个数-
范围内满足条件的数个数.我们设当前我们在求
范围内满足条件的数.
我们可以考虑一棵二叉树,往左走表示第位为
,往右走为
.(
表示深度;第
位表示最高位,以此类推;这里是
进制时).这样走到叶子节点都会得到一个数
,我们的任务就是找出恰好往右走了
次,结果得到的数不超过
的路径条数.
事实上我们不需要建出二叉树,只是这样可以使问题更形象
我们记录答案与一个计数变量
(表示前面有几位选了
).先将
在
进制下每一位求出来存在数组
中(
是最高位,
是次高位,共有
位),从
枚举到
,如果当前
,我们就有两种选择,一种是使当前位为
,还有一种是使当前位为
.如果
,i位及之后怎么走都不会超过
,于是答案就加上
并直接退出.如果
,选
的话答案加上
,选
的话使
加一并继续枚举低位.如果
,怎么样都得选
,于是继续枚举低位.注意两个问题:
的时候直接退出;最终
时要使答案+1(样例就有这种情况,算一下样例就明白了).
复杂度.(主要是预处理,事实上预处理阶乘就可以做到
,不过没什么必要)
代码
#include<bits/stdc++.h> using namespace std; #define i64 long long #define fp( i, b, e ) for ( int i(b), I(e); i <= I; ++i ) #define fd( i, b, e ) for ( int i(b), I(e); i >= I; --i ) #define go( i, b ) for ( int i(b), v(to[i]); i; v = to[i = nxt[i]] ) template<typename T> inline void cmax( T &x, T y ){ x < y ? x = y : x; } template<typename T> inline void cmin( T &x, T y ){ y < x ? x = y : x; } clock_t t_bg, t_ed; int K, B, X, Y, w[50], n, ans; int f[50][50]; int calc( int x ){ n = 0; while( x ) w[++n] = x % B, x /= B; int ans(0), c(0); fd( i, n, 1 ){ if ( w[i] == 1 ){ ans += f[i - 1][K - c]; if ( ++c > K ) return ans; } else if ( w[i] > 1 ) return ans += f[i][K - c]; } return ans + ( c == K ); } signed main(){ t_bg = clock(); scanf( "%d%d%d%d", &X, &Y, &K, &B ); f[0][0] = 1; fp( i, 1, 30 ){ f[i][0] = 1; fp( j, 1, 30 ) f[i][j] = f[i - 1][j] + f[i - 1][j - 1]; } printf( "%d\n", calc(Y) - calc(X - 1) ); t_ed = clock(); fprintf( stderr, "\n========info========\ntime : %.3f\n====================\n", (double)( t_ed - t_bg ) / CLOCKS_PER_SEC ); return 0; }