牛客国庆集训派对day5 C.Great Deceiver

题意: 给定 n n n k k k,问在 k k k进制和 − k -k k进制下值相同的数 x x x的个数, x ≤ n x\leq n xn
数据范围: 1 ≤ n ≤ 1 0 15 , 2 ≤ k ≤ 1000 1\leq n\leq 10^{15}, 2\leq k\leq 1000 1n1015,2k1000

题解: 转换成 k k k进制考虑下,发现只有当每个奇数位都为 0 0 0,即不会产生负数影响时的数才是合法的数,因此考虑转换成 k k k进制进行数位 d p dp dp,限制奇数位为 0 0 0即可。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<vector>
#include<cmath>
using namespace std;

typedef long long ll;

template<typename T>
inline T Read(){
    
    
    T s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {
    
    if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) {
    
    s = (s << 3) + (s << 1) + ch - '0'; ch = getchar();}
    return s * f;
}

#define read() Read<int>()
#define readl() Read<long long>()

ll n;
int k;

const int N = 70;
int bit[70];

ll dp[N];

ll dfs(int u, bool limit) {
    
    
	
	//到达了最后一个数  
	if(u == -1) return 1;
	
	//当没有到达顶端且这个数之前已经被计算过了 
	/*
		如 n = 3 4 5
		当前是 2 x y, 且u在x这个位置 
		但是之前 1 x y 时,x 和 y的所有合法情况都被考虑过了,因此不需要再次重复计算 
		故此时直接返回即可  
	*/ 
	if(!limit && dp[u] != -1) return dp[u];
	int last = limit ? bit[u] : (k - 1);
	if(u & 1) last = 0;
	ll ans = 0;
	
	for(int i = 0; i <= last; i++) {
    
    
		ans += dfs(u - 1, limit && i == bit[u]);
	}
	
	/*
	如上,这种情况下dp[u]会被多次使用,故记忆此次避免重复计算 
	*/
	if(!limit) dp[u] = ans;
	return ans;
}

void solve() {
    
    
	n = readl(), k = read();
	ll tn = n;
	int g = 0;
	while(tn) {
    
    
		bit[g++] = tn % k;
		tn /= k;
	}
	memset(dp, -1, sizeof dp);
	printf("%lld\n", dfs(g - 1, true));
}

int main()
{
    
    
	int T = 1;
	//T = read();
	for(int i = 1; i <= T; ++i) {
    
    
		solve();
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43900869/article/details/108930973