1585: 【例 1】Amount of Degrees

1585: 【例 1】Amount of Degrees

时间限制: 1000 ms 内存限制: 524288 KB
提交数: 683 通过数: 360
【题目描述】
原题来自:NEERC 2000 Central Subregional,题面详见 Ural 1057。

求给定区间 [X,Y] 中满足下列条件的整数个数:这个数恰好等于 K 个互不相等的 B 的整数次幂之和。例如,设 X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:

17=24+20
18=24+21
20=24+22
【输入】
第一行包含两个整数 X 和 Y,接下来两行包含整数 K 和 B。

【输出】
只包含一个整数,表示满足条件的数的个数。

【输入样例】
15 20
2
2
【输出样例】
3
【提示】
数据范围与提示:

对于全部数据,1≤X≤Y≤231−1,1≤K≤20,2≤B≤10。

思路:
数位dp原来不只是4维的qwq
f[i][j]表示枚举到第i位,当前有j个1的方案数
转移:f[i][j]=f[i-1][j]+[i-1][j-1]//表示放或者不放
注意,既然可以放也可以不放,那么必须满足对于当前这一位没有上界限制,即当前位数上的数>=2,如果当前位数上正好=1,那么就说明已经达到了上界,但是我们的dp是依赖没有达到上界来完成的,所以不可以转移,我们就直接把这一位当做0,然后f[i][k]=f[i-1][k],这样的话就满足未达到上界的条件啦。

注意!!!从高位到低位枚举!!!倒序!!!

代码:

#include<bits/stdc++.h>
#define ll long long
#define maxn 35
using namespace std;

ll x,y,k,b;
ll f[maxn][maxn],num;
ll c[maxn];

void prepare()
{
	memset(f,0,sizeof(f));
	f[0][0]=1;
	for(int i=1;i<=31;i++)
	{
		f[i][0]=1;
		for(int j=1;j<=i;j++)
		{
			f[i][j]=f[i-1][j-1]+f[i-1][j];
		}
	}
}

ll work(ll x,ll k)
{
	ll cnt=0;
	ll ans=0;
	while(x)
	{
		c[++cnt]=x%b;
		x/=b;
	}
	
	for(int i=cnt;i>=1;i--)
	{
		if(c[i]>1)
		{
			ans+=f[i-1][k]+f[i-1][k-1];
			break;
		}
		else if(c[i]==1)
		ans+=f[i-1][k],k--;
		if(k<0) break;
	}
	
	return ans;
	
}

int main()
{
	scanf("%lld%lld%lld%lld",&x,&y,&k,&b);
	prepare();
	printf("%lld",work(y+1,k)-work(x,k));
}

数位dp不难!!!
我才发现我不会的原因是我没用记忆化+dfs!!

#include<bits/stdc++.h>
#define maxn 35
#define ll long long
using namespace std;

ll f[maxn][maxn],c[maxn],x,y;
ll k,b,cnt;

ll dfs(ll n,ll num,bool up)
{
	if(n==0) return num==k;
	if(~f[n][num]&&!up) return f[n][num];
	int lim=up?c[n]:b-1;
	ll tmp=0;
	for(int i=0;i<=min(lim,1);i++)
	{
		tmp+=dfs(n-1,num+(i==1),up&&(i==lim));
	}
	if(!up) f[n][num]=tmp;
	return tmp;
}

ll work(ll x)
{
	cnt=0;
	while(x)
	{
		c[++cnt]=x%b;
		x/=b;
	}
	
	return dfs(cnt,0,1);
	
}

int main()
{
	memset(f,-1,sizeof(f)); 
	cin>>x>>y;
	cin>>k>>b;
	cout<<work(y)-work(x-1)<<'
';
	return 0;
}
原文地址:https://www.cnblogs.com/yxr001002/p/14436320.html