浏览器标题切换
浏览器标题切换end
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

HDU - 4507 吉哥系列故事——恨7不成妻

题面简述

题目链接

如果一个整数符合下面\(3\)个条件之一,那么我们就说这个整数和7有关——
  1、整数中某一位是\(7\)
  2、整数的每一位加起来的和是\(7\)的整数倍;
  3、这个整数是\(7\)的整数倍;

现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。

\(l,r\leq 10^{18}\)

具体请见题目

题解

典型的数位dp题目。

考虑\(f[p,s,v]\)表示数位\(p\),各位和对\(7\)取模是\(s\),数对\(7\)取模是\(v\)的方案数。

那么在搜索的过程中只要避开\(7\)的分支,并在搜到叶节点的时候判断是否满足条件\(2,3\)即可。

如果是和显然很好求。平方和需要用到额外的东西。

完全平方公式

大概是初二的时候教了这东西吧。完全没想到它\((a+b)^2=a^2+b^2+2ab\)

那么利用这个公式。我们就可以在中途维护这个平方和了。

\(f\)数组维护三个量:\(cnt\)表示合法的数的个数,\(sum\)表示合法的数的和,\(sumq\)表示合法的数的平方和。

那么在回溯过程中对\(cnt\)\(sum\)累加,对\(sumq\)利用完全平方公式处理即可。

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;

ll num[23], cnt, po[23];
ll l, r;
struct task { 
	ll cnt, sumq, sum;
	task() {cnt = -1; sumq = sum = 0;} 
	//cnt 个数 sumq 平方和 sum和 
	//(a+b)^2=a^2+b^2+2ab
}f[23][23][23];

void init() {
	for(int i = 0; i <= 22; ++i) {
		for(int j = 0; j <= 22; ++j) {
			for(int k = 0; k <= 22; ++k) {
				f[i][j][k].cnt = -1;
				f[i][j][k].sumq = f[i][j][k].sum = 0;
			}
		}
	}
}

task dfs(int pos, bool done, ll sum, ll v) {
	// pos位 done判定上界 sum和对7的mod, v mod 7的值 
	if(!pos) {
		task tmp;
		tmp.cnt = sum && v;
		tmp.sum = tmp.sumq = 0;
		return tmp;
	}
	if(!done && f[pos][sum][v].cnt != -1) return f[pos][sum][v];
	ll ed = done ? num[pos] : 9; task res;
	res.cnt = 0;
	for(ll i = 0; i <= ed; ++i) {
		if(i == 7) continue;
		task tmp = dfs(pos - 1, done && (i == ed), (sum + i) % 7, (v * 10ll + i) % 7);
		res.cnt += tmp.cnt;
		res.cnt %= mod; 
		res.sum += tmp.sum + ((po[pos] * i) % mod * tmp.cnt) % mod;
		res.sum %= mod;
		res.sumq += tmp.sumq; res.sumq %= mod;
		res.sumq += (po[pos]*i%mod) * (po[pos]*i%mod) % mod * tmp.cnt % mod; res.sumq %= mod;
		res.sumq += 2ll * (po[pos]*i) % mod * tmp.sum % mod; res.sumq %= mod;
	}
	if(done) return res;
	return f[pos][sum][v] = res;
}

ll count(ll x) {
	init();
	cnt = 0;
	while(x) num[++cnt] = x % 10, x /= 10;
	return dfs(cnt, 1, 0, 0).sumq;
}

int main() {
	int T;
	po[1] = 1;
	for(int i = 2; i <= 22; ++i) po[i] = po[i - 1] * 10ll % mod;
	cin >> T;
	while(T--) {
		cin >> l >> r;
		cout << (count(r) - count(l - 1) + mod) % mod << endl;
	}
	return 0;
}

posted @ 2019-05-05 17:04  henry_y  阅读(165)  评论(0编辑  收藏  举报