[CF55D]Beautiful numbers

題目

傳送門 to CF

傳送門 to VJ

題意概要
[a,b][a,b] 中多少個數滿足,若非零整數 xx 在其十進制表示下出現過,則 xx 爲其因數。

數據範圍與約定
數據組數 T10T\le 101ab9×10181\le a\le b\le 9\times10^{18}

思路

我最初的想法是,十二維 dpdp ,用 f(S,x1,x2,x3,,x9,y)f(S,x_1,x_2,x_3,\dots,x_9,y) 表示,SS 是已經出現過的數字(狀壓),xix_i 是原來的數模 ii 的值,而 yy 表示是否已經小於上界。考慮到第幾位,滾動數組優化掉——我往往這麼做。你想試試嗎?

仔細思考一下,我們並不在乎模每一個已經出現過的數字的餘數。因爲我們要求它們全部都是零。所以,我們應該考慮 模其最小公倍數的值

然而不能走一步就模一次,不然最小公倍數發生變化就慘了。比如 x=6,xmod2=0,xmod7=6x=6,x\bmod 2=0,x\bmod 7=6 ,但是 (xmod2)mod7=0(x\bmod 2)\bmod 7=0 就不等於 xmod7x\bmod 7 的值了。

然而數字集是很小的。不難發現所有的最小公倍數都是 25202520 的因數,所以模 25202520 行得通。

得預處理一下所有可能的 lcmlcm 。有多少個?可以直接計算:23,32,51,712^3,3^2,5^1,7^1 是有可能出現的質因數分解,所以可能的情況是 4×3×2×2=484\times 3\times 2\times 2=48 個。

發現狀態數明顯下來了!用 f(x,y)f(x,y) 表示,原數模 25202520 的結果是 xx ,最小公倍數爲 yy

代碼

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
inline int readint() {
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
void writeint(int x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar((x%10)^48);
}
# define MB template < typename T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }
# define FOR(i,n) for(int i=0; i<(n); ++i)
# define LB lower_bound
typedef long long int_;
int getLcm(int a,int b){ return a/__gcd(a,b)*b; }

const int m = 48; // 不同的lcm的數量
const int all = 2520; // 最大的lcm(所有lcm的lcm)
int_ tmp[all][m][2], dp[all][m][2];
int lcm[m] = {1,2,3,4,5,6,7,8,9,10,12,14,15,18,20,
	21,24,28,30,35,36,40,42,45,56,60,63,70,72,84,
	90,105,120,126,140,168,180,210,252,280,315,360,
	420,504,630,840,1260,2520
}; // 打個表就行咯
int __lcm[m][10]; // lcm[i]與j的lcm
int f[all+1]; // O(1)的對應回lcm

int d[19];
int_ work(int_ num){
	for(int i=18; ~i; --i)
		d[i] = num%10, num /= 10;
	static const int siz = all*m<<4;
	memset(dp,0,siz), dp[0][0][0] = 1;
	for(int x=0; x<19; ++x){
		memset(tmp,0,siz);
		FOR(now,10) FOR(i,all) FOR(j,m){
			int i_ = (i*10+now)%all;
			int j_ = __lcm[j][now];
			int_ *p = tmp[i_][j_];
			if(now <= d[x])
				p[now<d[x]] += dp[i][j][0];
			p[1] += dp[i][j][1];
		}
		swap(dp,tmp); // dp = tmp
	}
	int_ res = 0;
	FOR(j,m) for(int i=0; i<all; i+=lcm[j])
		res += dp[i][j][1];
	return res;
}

int main(){
	FOR(i,m) f[lcm[i]] = i;
	FOR(i,m) FOR(j,10) if(j != 0)
		__lcm[i][j] = f[getLcm(lcm[i],j)];
	FOR(i,m) __lcm[i][0] = i; // 不變
	for(int T=readint(); T; --T){
		int_ x, ans = 0;
		cin >> x, ans -= work(x);
		cin >> x, ans += work(x+1);
		cout << ans << endl;
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章