dp之数位dp---(度的数量)

题目:

在这里插入图片描述

思路:
首先对题目意思进行分析:恰好等于 K 个互不相等的 B 的整数次幂之和,这句话的意思是说对于某一个B进制数x说,x只有k位上只有1,其他位为0。然后让你求区间[X,Y]的满足条件的数字有多少,这里我们可以用区间[1,Y]-[1,X-1]来更加容易的得到答案。

那么我们怎么获得前n个数的满足条件的数呢,首先将B进制数x中的 每一位用a表示为anan-1an-2an-3…a0,然后从第一位an开始,把它分为两部分0-an-1和an,所有的决策都可以由第一部分和第二部分的和组成(图片画横线的部分)。

当我们选第一部分0-an-1的时候,那么第an-1位以及以后的数字我们可以任意选取,(不会超过x),但是因为题目要求只能选0或者1那么先取0的话就是n个数(因为最后一个数的下标为a0)中选出k-last(当前已经选了多少个1)个1的结果,如果取1的话那么就是n个数中选出k-last个1的结果,这部分结果可以用组合数来求得(根据组合数公式C(a,b)=C(a-1,b)+C(a-1,b-1)来获得),但是取1有个前提条件,那么就是an必须大于1(因为如果an等于1的话,那么他是会分到第二部分的)。

这样第一部分就选完了,那么对于第二部分an的时候,如果an不为1,那么根据题意不能选择除了0或者1之外的任意数字,那么直接break退出即可;否则如果为1,那么last就要++(接下来需要的1少一个),然后继续往下走每次计算左边的决策,然后走到最后一位的时候a0如果选0,那么就可以根据判断条件[last=k]是否成立,如果成立结果res++即可;如果选1,那么也会last++,然后因为走到了最后一位,后面没有可以再进行的决策,那么还是根据[last=k]是否成立来使res++。
在这里插入图片描述
大体思路就是这样子的,具体细节可以看看代码:

/**
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃  	    ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*/

#include<cstdio>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include<vector>
#include<queue>
#include<map>
#define sc_int(x) scanf("%d", &x)
#define sc_ll(x) scanf("%lld", &x)
#define pr_ll(x) printf("%lld", x)
#define pr_ll_n(x) printf("%lld\n", x)
#define pr_int_n(x) printf("%d\n", x)
#define ll long long 
using namespace std;

const int N=1000000+100;
int x,y,k,b;
int s[40][40];//因为如果是二进制位可能会有30位多开了点

void cal()//用于计算组合数 
{
	for(int i =0;i<=40;i++)
		for(int j =0;j<=i;j++)
		if(j==0)s[i][j]=1;//i个数中不选也有一种方案
		else s[i][j]=s[i-1][j]+s[i-1][j-1];
}

int dp(int n)
{
	if(n==0)return 0;//特判,0的时候没有任何一种方案

	vector<int>cnt;//用来存储进制转换后的数

	while(n)cnt.push_back(n%b),n/=b;

	int res=0;//结果
	int last=0;//到当前情况已经选了多少个1

	for(int i =cnt.size()-1;i>=0;i--)//从最高位开始运算
	{
		int x=cnt[i];
		if(x)//如果x大于等于1
		{
			res+=s[i][k-last];//i个数中选k-last个1
			if(x>1)
			{
				if(k-last-1>=0) res+=s[i][k-last-1];//选1的情况就少了一个1可以选择,
				//到此左半部分计算完毕
				break;//因为x大于1的时候右半部分不能得到1,直接退出即可
			}
			else{//x为1 的情况
				last++;//已经选了的1增加
				if(last>k)break;//如果已经选了1的数量大于k那么也可以直接退出
			}
		}
		if(i==0&&k==last)res++;//走到了a0的情况判断一下是否可以构成一种方案
	} 
	return res;
}

int main()
{
	cal();
	cin>>x>>y>>k>>b;
	cout<<dp(y)-dp(x-1)<<endl;

	return 0;
}

看y总的大概思路就是把这些数按树形结构分成两部分考虑,每一部分的答案根据上一部分获得,反正自己也不是很会,只能尽量写了那么清楚了qwq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值