1. 程式人生 > >[JZOJ2700] 【GDKOI2012模擬02.01】數字

[JZOJ2700] 【GDKOI2012模擬02.01】數字

題目

在這裡插入圖片描述

題目大意

其實這題的題目大意非常簡練,所以我認為我不用解釋了。


思考歷程

首先亂推了一波,然後什麼東西都沒有發現。
於是想想 D ( i ) D(i) 的性質。
我發現,由於每次是將各位上的數字相加。所以最多操作三次。
本來是一個很大的數,然後縮小成百位數,然後縮成十位數,最後縮成個位數。
我想,既然縮小一次就成了百位數了,所以,為什麼不直接把百位數的表打出來,然後再繼續推式子呢?
然後我就把表打了出來。
於是我就發現,我前面的想法盡是沒用的……
因為我發現了一個顯而易見的規律: D

( i ) = ( i 1 ) m
o d
   9 + 1 D(i)=(i-1) \mod 9+1
這個規律可以感性理解,也可以理性證明,反正很簡單,我就不說了。
所以說,一個“被喜歡的數”就是能被 x
( ( x 1 ) m o d    9 + 1 ) x*((x-1)\mod 9+1)
表示的數。
接下來就開始了我的瞎搞歷程(提醒一下,正確性有誤……)
由於 D ( x ) < = 9 D(x)<=9 ,不妨列舉 D ( x ) D(x) ,設為 j j
設現在的數為 i i 。顯然,如果要成立,首先要滿足 i m o d    j = 0 i \mod j=0
然後亂推: ( i j 1 ) m o d    9 + 1 = j \left(\frac{i}{j}-1\right) \mod 9+1 =j
所以 ( i j 1 ) m o d    9 = j 1 \left(\frac{i}{j}-1\right) \mod 9=j-1
由於 j 1 < 9 j-1< 9 ,所以 i j 1 j 1 ( m o d    9 ) \frac{i}{j}-1 \equiv j-1 (\mod 9) ,所以 i j j ( m o d    9 ) \frac{i}{j} \equiv j (\mod 9)
然後就是最尷尬的步驟: i j 2 ( m o d    9 ) i\equiv j^2(\mod 9)
所以說,如果 i i 滿足條件,必定有一個 j j 使得 i m o d    j = 0 i \mod j=0 i j 2 ( m o d    9 ) i\equiv j^2(\mod 9)
哈,這東西好像可以DP!
f i , j , k f_{i,j,k} 表示到第 i i 位,模 2520 2520 的餘數為 j j ,第 i i 位上的值為 k k 的數的個數。
2520 2520 1 1 9 9 的最小公倍數)
按照之前推出來的條件,我們可以發現它是否為“被喜歡的數”只和 j j 有關。
那我就可以愉快地數位DP了。

然而現實是殘酷的……
WA了……
後來推了好久,我發現原來是上面的一步出現了問題:
我們知道 i j j ( m o d    9 ) \frac{i}{j} \equiv j (\mod 9) ,可以推出 i j 2 ( m o d    9 ) i\equiv j^2(\mod 9) 。可是後者不一定推出前者。
因為 9 9 不是質數……
不過如果只有這點錯誤,隨便改一改那似乎也是可以過得去的。
然後我就發現原來還是需要判重!
怎麼判?數位DP怎麼判?判不了啊!

XC說,今天除了第一題之外,其他的題還是很有難度的。
除了第一題!!!!!!


正解

先說一個別人家的正解(當然我不懂是為什麼):
就是打一波表,然後發現,咦,原來是有迴圈節的!
然後就隨隨便便的搞定了……

然後再說一個正經一些的做法:
首先對於一個數 x D ( x ) x*D(x) ,我們可以將其表示為 ( 9 t + D ( x ) ) D ( x ) (9t+D(x))*D(x)
D ( x ) D(x) 的取值是很少的,也就只有 9 9 種。
我們把它當成常數來看,然後就變成 9 D ( x ) t + D 2 ( x ) 9D(x)*t+D^2(x) ,變成 a t + b at+b 的形式。
對於一個 D ( x ) D(x) ,我們可以很容易地計算出它在某一個區間裡的貢獻。
然後我們要去重。
如何去重?容斥原理,將一些式子合併一下就可以了。用擴充套件中國剩餘定理就好。
可以手打擴充套件中國剩餘定理,其實也是可以推出來的嘛……
可是某些機智懶惰的同學發現了一個好方法:
我們將所有的 D ( x ) D(x) 的式子列出來,然後將它們都模 9 9
然後就會驚奇地發現下面的這張表:
1 4 0 7 7 0 4 1 0
只有模數相同的有可能可以合併。
所以運算量大大減少……
然後我就全部手推出來了。具體見程式(有的式子合併之後無解,我也有註釋)。
然後這題就愉快地解決了。


程式碼

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
struct func{
	int a,b,ty;
} d[20];
int cnt;
inline long long getans(long long lim){
	long long res=0;
	for (int i=1;i<=cnt;++i)
		if (lim-d[i].b>=0)
			res+=((lim-d[i].b)/d[i].a+1)*d[i].ty;//計算貢獻……不用解釋
	return res;
}
int main(){
	for (int i=1;i<=9;++i)
		d[++cnt]={i*9,i*i,1};
	d[++cnt]={72,64,-1};//d[1] and d[8]
	d[++cnt]={126,112,-1