奇怪的数位dp CF908G New Year and Original Order

35 篇文章 2 订阅
31 篇文章 0 订阅

CF908G New Year and Original Order

前言:

一道2800的数位dp,说实话做的时候还是挺虚的,但是做着做着就过了?果然数位dp的rating都是虚高

大意:

求区间[L,R]内每一个数字在各数位排序后得到的数的和,答案对1e9+7取模

n<=1e700

思路:
首先这种题目是存在一定套路的,但是没见过基本是想不出来的(估计也是其rating高的原因)

这道题唯一的性质就是每一个数字的各个数位是升序排列的,然后我们可以发现这么一个结论:

每一个数字都可以被最多9个由1组成的数累加得到

举个例子,23349,可以按照如下方式拆分

最多只有9个是因为每一位最大只有9。然后我们看一下每一行的1的个数,其实也不难发现,第i行的1的个数=大于等于i的数字的个数,手模一下就可以验证了。

所以在第d行,计数字x中>=d的数字的个数为k,则x在第d行的贡献就是(10^k-1)/9(其实就是在构造k个1)

在此基础上,我们分9次来计算每一行的贡献。对于第d行,设dpi,j表示前i位,有j位>=d,直接做数位dp即可

所以此题的关键就是用1来组成数字,剩余的工作就都是很基础的了。不好想,只能说留个印象。

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
const ll mod=1e9+7;
string n;
ll a[710];
ll cnt=0;
ll dp[710][710]; 
ll p[710];
ll inv9;
ll ksm(ll x,ll y)
{
	ll ans=1;
	while(y)
	{
		if(y&1) ans=ans*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ans;
}
ll inv(ll x)
{
	return ksm(x,mod-2);
}
void init()
{
	inv9=inv(9); 
	ll now=1;
	for(int i=1;i<=710;++i)
	{
		p[i]=((now*10%mod)-1+mod)%mod*inv9%mod;
		now=now*10%mod;
	}
//	for(int i=1;i<=10;++i) cout<<p[i]<<endl;
}
ll dfs(ll x,ll op,ll pre,ll d)
{
	if(!x) return p[pre];
	if(!op&&dp[x][pre]!=-1) return dp[x][pre];
	ll tot=0;
	ll lim=op?a[x]:9;
	for(int i=0;i<=lim;++i)
	{
		if(i<d) tot=(tot+dfs(x-1,op&&i==lim,pre,d))%mod;
		else tot=(tot+dfs(x-1,op&&i==lim,pre+1,d))%mod;
	}
	if(!op) dp[x][pre]=tot;
	return tot;
}
ll f(string s)
{
	cnt=0;
	int len=s.size();
	for(int i=len-1;i>=0;--i)
	{
		a[++cnt]=s[i]-'0';
	}
	ll ans=0;
	for(int i=1;i<=9;++i)
	{
		memset(dp,-1,sizeof dp);
		ans=(ans+dfs(cnt,1,0,i))%mod;
	}
	return ans;
}
void solve()
{
	init();
	cin>>n;
	cout<<f(n)<<endl;
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	solve();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值