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;
}