题目链接:Problem - 855E - Codeforces
题意:就是给你一个l和r以及b,求在b进制下的数l<=k<=r满足0~b-1都出现偶数次的数有多少个
这显然是一道数位DP,其实这道题我一开始理解错题意了,出现偶数次按道理说应该是出现过的,没出现过应该是不符合题意的,而这道题却将正确答案包括了0~b-1种的一些数未出现的情况,这一点确实是题意表述不清了,因为我之前做过一道类似的题,求的是l到r内奇数位出现偶数次和偶数位出现奇数次的数,而那道题目中一定要出现才算,建议大家做一下那道题,还是挺好的一道题:(SPOJ - BALNUM)Balanced Numbers(数位DP+状态压缩)_AC__dream的博客-CSDN博客
最后想说的一点就是,假如f[i][j][表示遍历到第i位,出现次数状态为j的合法方案数,那么对于每次询问,由于b可能不同,所以按道理我们应该每次都初始化f数组,但是这样会超时,我们应该在f数组上加上一维表示进制的数,f[i][j][k]表示遍历到第i位,出现次数状态为j在k进制下的方案数,这样我们就只需要初始化一次f,且当询问到相同的进制时我们还能使用原来已经计算好的值,会对程序起到一定的优化作用,这也是一种数位DP优化方法,还是值得思考的
下面是代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
ll f[103][1<<11][10];//f[i][j][k]表示遍历到第i位,出现次数状态为j在k进制下的方案数
int a[103];
int b;
ll dp(int pos,int s,int lead,int limit)
{
if(!pos) return (s==0);
if(!lead&&!limit&&f[pos][s][b]!=-1) return f[pos][s][b];
ll ans=0;
int up=limit?a[pos]:(b-1);
for(int i=0;i<=up;i++)
{
if(lead&&i==0)
ans+=dp(pos-1,s,lead,limit&&(i==up));
else
ans+=dp(pos-1,s^(1<<i),lead&&(i==0),limit&&(i==up));
}
if(!lead&&!limit) f[pos][s][b]=ans;
return ans;
}
ll solve(ll x)
{
int pos=0;
while(x)
{
a[++pos]=x%b;
x/=b;
}
return dp(pos,0,1,1);
}
int main()
{
ll l,r;
int T;
cin>>T;
memset(f,-1,sizeof f);
while(T--)
{
scanf("%d%lld%lld",&b,&l,&r);
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}