【数位dp?】CodeForces 288E Polo the Penguin and Lucky Numbers

8 篇文章 0 订阅
5 篇文章 0 订阅

题目点这里


那么好吧终于把这道题折腾完了

以及我并不觉得这是dp = =


题意:所有数位仅含4和7的数为lucky number 设[L, R]中第i大的lucky number为ai 求 a1*a2 + a2*a3 + a3*a4 +... an-1*an


L和R的范围是10的10000次方 好吧 这数据范围就是数位dp的标准范围了 = =(不!其实一般的数位dp根本没这么变态!)

然后就想要怎么维护这个相邻两项乘积的和……嗯…………好像和平方减去平方和有点像…………展开一看……好像不太对QAQ……

然后我去问了鲲哥 小明 翔爷 = =嗯……都没想法………………

于是去看了官方题解……maya英文渣跪了…………

 = =然后看ac code。。。maya这些预处理都是在搞什么鬼…………= =


感觉喵生没有希望了。


然后lhy同学。。看了那些代码 = =十分神奇地写出了一个公式

然后拆拆拆。。。推推推………………居然就…………推出来了………………

orz让我跪跪

orz其实根本不需要预处理

orz其实递推就可以了。。



让我来整理一下思路。。。(反正也不是我想出来的T_T)


首先 和大部分(不是所有)的数位dp一样 这个答案是可以相减的

也就是说 可以分开处理L和R再相减就可以了

但是这里的答案是solve(R) - solve(L)而不是L - 1 因为上界an * an+1是不算在答案里的而下界a1 * a2是算在答案里的


假设我们现在要求的是solve(X) 认为比X小的并且与X位数相等的幸运数字都是合法的(注意L和R位数是一样的)

把X的前n位取出来看成一个数C(比如说 X = 477444 i = 3时 C = 477)设Fn表示solve(C) Kn表示比C小与C位数相等的幸运数字个数。由定义 有:

如果已知Fi 那么 当X[n + 1] == 4时 有:


当X[n + 1] == 7时 有:



其实差别只在第一个sigma的上标 那么先考虑X[n + 1] == 4的情况:


将之前的定义式带入得:


然后可以发现:


那么带上去得:



然后。。。我们维护一个ai的和和一个ai^2的和再记录akn和a1还有kn 就可以递推了……

关于怎么维护……暴力拆一下 = =




然后……当X[i + 1] == 7的时候 我们把最后多出来的(10*ak+4)(10*ak+7)算上就行了。注意 ai == 10*ak+4而不是10*ak+7


因为我怕爆 long long 又觉得中途取模不好看。。所以用的是 unsigned long long = =但是乘的时候还是爆了T_T。。。。所以中途还是取了模。。。。。

那么其实代码一共只有39行。。但是它背后却是上面一堆乱七八糟丧心病狂非人哉的公式…………

#include <cstdio>
#include <iostream>
#include <string>

using namespace std;

typedef unsigned long long ULL;
const int Mod = 1000000007;

ULL solve()
{
	string s; cin >> s; int len = s.size();
	ULL F = 0llu, suq = 0llu, sum = 0llu, ak = 0llu, a1 = 0llu, K = 1llu;
	for(int i = 0; i < len; ++i)
	{
		F = 100 * F + 100 * suq + 220 * sum + 70 * (ak + Mod - a1) % Mod + 56 * (K - 1); F %= Mod;
		suq = 200 * suq + 220 * sum + 65 * (K - 1); suq %= Mod;
		sum = 20 * sum + 11 * (K - 1); sum %= Mod;
		
		if(s[i] ^ '4')
		{
			F += ((ak * 10 + 4) % Mod) * ((ak * 10 + 7) % Mod); F %= Mod;
			suq += ((ak * 10 + 4) % Mod) * ((ak * 10 + 4) % Mod); suq %= Mod;
			sum += ak * 10 + 4; sum %= Mod;
		}
		
		ak = ak * 10 + s[i] - '0'; ak %= Mod; a1 = a1 * 10 + 4; a1 %= Mod;
		K = (K - 1) * 2; K += (s[i] == '4') ? 1 : 2; K %= Mod; 
	}
	return F;
}

int main()
{
	ios :: sync_with_stdio(false);
	ULL res = -solve(); res += solve();
	cout << (res + Mod) % Mod << endl;
	return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值