P4999烦(gui)人(chu)的数学作业
P4399P4999
这是一道有着三倍经验的宝藏题目
我们可以求出来1到n中,1~9分别出现了几次,设f[i]为数字i出现的次数,则\(ans=\sum{f[i]\cdot i}\)
然后就是数位dp干的事了
我们可以在dp时统计当前要求的数goal出现的次数sum,到达边界时返回sum即可。注意考虑前导零
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int inf=214748364;
const ll mod=1000000007;//注意是1e9+7
inline ll read()
{
char ch=getchar();
ll x=0;bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-')f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?-x:x;
}
ll l,r;
ll f[10],g[20][250],li[20];
int t;
void make(ll k)
{
t=0;
while(k)
{
li[++t]=k%10;
k/=10;
}
}
ll dfs(int now,int sum,int goal,bool lim,bool al)//al记录是否前面全是0
{
if(!now)return sum;
if(!al&&!lim&&g[now][sum]!=-1) return g[now][sum];
int up=lim?li[now]:9;
ll rtn=0;
for(int i=0;i<=up;i++)
{
rtn+=dfs(now-1,sum+((i||(!al))&&(i==goal)),goal,lim&&(i==up),al&&(!i));//这里sum的计算方式是在考虑goal是0的时候,排除前导零的影响(在另外那两倍经验那里也适用)
rtn=(rtn+mod)%mod;
}
if(!lim&&!al) g[now][sum]=rtn;
return rtn;
}
int main()
{
int fk=read();
while(fk--)
{
ll ans=0;
l=read();
r=read();
make(r);
for(int i=1;i<=9;i++)
memset(g,-1,sizeof(g)),f[i]=dfs(t,0,i,1,1);
if(l>1)
{
make(l-1);
for(int i=1;i<=9;i++)
memset(g,-1,sizeof(g)),f[i]-=dfs(t,0,i,1,1);
}
for(int i=1;i<=9;i++)
ans=(ans+((f[i]%mod)*i+mod)%mod+mod)%mod;
printf("%lld\n",ans);
}
}
另外的两倍经验是P2602和P1239
手动滑稽