HDU4734 F(x)
题意&思路:
f
(
x
)
f(x)
f(x)的定义是,对于十进制数的每位
(
a
n
a
n
−
1
…
…
a
1
)
(a_na_{n-1}……a_1)
(anan−1……a1),
f
(
a
)
=
a
n
∗
2
n
−
1
+
a
n
−
1
∗
2
n
−
2
+
…
…
+
a
1
∗
2
0
f(a)=a_n*2^{n-1}+a_{n-1}*2^{n-2}+……+a_1*2^0
f(a)=an∗2n−1+an−1∗2n−2+……+a1∗20。给你a和b,求在
f
(
x
)
<
=
f
(
a
)
,
x
∈
[
0
,
b
]
f(x)<=f(a),x∈[0,b]
f(x)<=f(a),x∈[0,b]的个数。
又是数位dp,依旧没有思路。看了大佬的思路,惊为天人。
用dp[i][j]表示第i位,j是后面要凑的权值和。
需要的
f
(
a
)
f(a)
f(a)和你现在的总权值的差值,就是你在接下来几位该凑的权值,至于怎么凑成,我们并不用在意,因为可以通过前面的记忆化来实现。这样就分成了一个个小的子集去求和。
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
const int N=1e4+10;
const int mod=1e7+9;
const int maxn=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
const int inf=99999999;
using namespace std;
int ask,a[20],dp[20][N];
int change(int x)
{
int k=1,ans=0;
while(x)
{
ans+=k*(x%10);
x/=10;
k*=2;
}
return ans;
}
int dfs(int len,int sum,bool limit)
{
if(len==0) return sum<=ask;
if(sum>ask) return 0;
if(!limit && dp[len][ask-sum]) return dp[len][ask-sum];
int up=limit?a[len]:9,i,temp=0;
for(i=0;i<=up;i++)
temp+=dfs(len-1,sum+i*(1<<(len-1)),limit && i==a[len]);
if(!limit) dp[len][ask-sum]=temp;
return temp;
}
int solve(int x)
{
int l=0;
while(x)
{
a[++l]=x%10;
x/=10;
}
return dfs(l,0,true);
}
int main()
{
int t,i;
scanf("%d",&t);
for(i=1;i<=t;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ask=change(x);
printf("Case #%d: %d\n",i,solve(y));
}
return 0;
}