HDU 6156 Palindrome Function
原题连接
http://acm.hdu.edu.cn/showproblem.php?pid=6156
这是一个经典的数位 dp
∑i=LR∑j=lrf(i,j)=∑j=lr∑i=LRf(i,j)
我们记:
slove(n,k)=∑i=1nf(i,k)
则:
∑j=lr∑i=LRf(i,j)=∑j=lr(slove(R,j)−slove(L−1,j))
通过数位 dp ,可以快速 O(logkN) 计算 slove(N,k)
记含有前导
0
的长度为len 的
k
进制回文数字数量为:dp[k][len]
令: dp[k][1]=k,dp[k][0]=1
则: dp[k][len]=dp[k][len−2]∗k , len>1
记不含有前导
0
的长度为len 的
k
进制回文数字数量为:
P[k][len]
则 P[k][0]=0,P[k][1]=k−1
P[k][len]=dp[k][len−2]∗(k−1) 这是因为两端可选范围是 [1,k−1]
记不含有前导
0
长度不大于len 的
k
进制回文数字的数量为
D[k][len]
D[k][len]=∑i=0lenP[k][i]
记 kt≤n<kt+1
记。含有前导
0
的不超过n 的。
k
进制回文数的数量为
DFS(n,k,t)
这里。
t
是用来确定kt≤n<kt+1
令 u=⌊nkt⌋
对于最高位确定。意味着最低位也取相同数字。
所以一次确定两个数字。
最高位取
[0,u−1]
时。中间数字可以随意取。都不会超过
n
的范围。
这部分对答案的贡献为:
dp[k][t−1]∗u
对于最高位为
u
时.
因为ukt≤n<(u+1)kt
我们记最高位为
u
时。中间数字可以取到的最大值为:(除去最高最低位)
r=n mod ktk−[n mod k<u]
此时我们可以得到一个子问题:
DFS(n,k,t)=dp[k][t−1]∗u+DFS(r,k,t−2)
特别的:
DFS(n,k,0)=n+1DFS(n,k,1)=⌊nk⌋+[⌊nk⌋≤n mod k]
现在我们解决 slove(n,k)
令 kt≤n<kt+1
令 u=⌊nkt⌋
因为不能含有前导
0
.幸运的是。我们已经计算了小于kt 的所有不含前导0的回文数的数量。
D[k][t]
对于最高位取
[1,u−1]
时。这部分贡献位:
dp[k][t−1]∗(u−1)
对于最高位取
u
时。中间一部分的取值最大值为:r=n mod ktk−[n mod k<u]
所以:
slove(n,k)=D[k][t]+dp[k][t−1]∗(u−1)+DFS(r,k,t−2)
特别的。
n<k时:slove(n,k)=nn<k2时:slove(n,k)=k+⌊nk⌋+[⌊nk⌋≤n mod k]−2
搞定。
下面是代码:
#include <algorithm>
#include <string.h>
#include <stdio.h>
#define MAXN 100
using namespace std;
typedef long long LL;
const LL inf=40*1e9;
LL dp[MAXN][MAXN];
LL D[MAXN][MAXN];
LL bit[MAXN][MAXN];
int size[MAXN];
void init();
LL DFS(LL n,int k,int b)
{
if(b==0)return n+1;
if(b==1)return n/k+(n/k<=n%k);
LL u=n/bit[k][b];
LL r=n%bit[k][b];
LL ans=u*dp[k][b-1];
r/=k;
if(u>n%k)r--;
return ans+DFS(r,k,b-2);
}
LL slove(LL n,int k)//小于等于n的k进制数回文数的个数
{
if(n<(LL)k) return n;
if(n<((LL)k*k)) return k-1+n/k+(n/k<=n%k)-1;
for(int i=size[k];i>-1;i--)
if(n>=bit[k][i])
{
LL u=n/bit[k][i];
LL ans=D[k][i]+(u-1)*dp[k][i-1];
LL r=(n%bit[k][i])/k;
if(n%k<u)r--;
return ans+DFS(r,k,i-2);
}
return 0;
}
int main ()
{
init();
int t,deep=1;
scanf("%d",&t);
while(t--)
{
int L,R,l,r;
scanf("%d %d %d %d",&L,&R,&l,&r);
LL ans=0;
L--;
for(int i=l;i<=r;i++)
{
LL cnt=slove((LL)R,i)-slove((LL)L,i);
ans+=cnt*i;
ans+=R-L-cnt;
}
printf("Case #%d: %lld\n",deep++,ans);
}
return 0;
}
void init()
{
for(int i=2;i<37;i++)
{
bit[i][0]=1;
while(bit[i][size[i]]<=inf)
{
size[i]++;
bit[i][size[i]]=bit[i][size[i]-1]*(LL)i;
}
}
for(int i=2;i<37;i++)
{
dp[i][0]=1;
dp[i][1]=i;
D[i][0]=1;
D[i][1]=i-1;
for(int j=2;j<=size[i];j++)
{
dp[i][j]=dp[i][j-2]*i;
D[i][j]=D[i][j-1]+dp[i][j-2]*(i-1);
}
}
}