[JZOJ2700] 【GDKOI2012模擬02.01】數字
題目
題目大意
其實這題的題目大意非常簡練,所以我認為我不用解釋了。
思考歷程
首先亂推了一波,然後什麼東西都沒有發現。
於是想想
的性質。
我發現,由於每次是將各位上的數字相加。所以最多操作三次。
本來是一個很大的數,然後縮小成百位數,然後縮成十位數,最後縮成個位數。
我想,既然縮小一次就成了百位數了,所以,為什麼不直接把百位數的表打出來,然後再繼續推式子呢?
然後我就把表打了出來。
於是我就發現,我前面的想法盡是沒用的……
因為我發現了一個顯而易見的規律:
這個規律可以感性理解,也可以理性證明,反正很簡單,我就不說了。
所以說,一個“被喜歡的數”就是能被
表示的數。
接下來就開始了我的瞎搞歷程(提醒一下,正確性有誤……)
由於
,不妨列舉
,設為
。
設現在的數為
。顯然,如果要成立,首先要滿足
。
然後亂推:
所以
由於
,所以
,所以
然後就是最尷尬的步驟:
所以說,如果
滿足條件,必定有一個
使得
且
。
哈,這東西好像可以DP!
設
表示到第
位,模
的餘數為
,第
位上的值為
的數的個數。
(
是
到
的最小公倍數)
按照之前推出來的條件,我們可以發現它是否為“被喜歡的數”只和
有關。
那我就可以愉快地數位DP了。
然而現實是殘酷的……
WA了……
後來推了好久,我發現原來是上面的一步出現了問題:
我們知道
,可以推出
。可是後者不一定推出前者。
因為
不是質數……
不過如果只有這點錯誤,隨便改一改那似乎也是可以過得去的。
然後我就發現原來還是需要判重!
怎麼判?數位DP怎麼判?判不了啊!
XC說,今天除了第一題之外,其他的題還是很有難度的。
除了第一題!!!!!!
正解
先說一個別人家的正解(當然我不懂是為什麼):
就是打一波表,然後發現,咦,原來是有迴圈節的!
然後就隨隨便便的搞定了……
然後再說一個正經一些的做法:
首先對於一個數
,我們可以將其表示為
。
的取值是很少的,也就只有
種。
我們把它當成常數來看,然後就變成
,變成
的形式。
對於一個
,我們可以很容易地計算出它在某一個區間裡的貢獻。
然後我們要去重。
如何去重?容斥原理,將一些式子合併一下就可以了。用擴充套件中國剩餘定理就好。
可以手打擴充套件中國剩餘定理,其實也是可以推出來的嘛……
可是某些機智懶惰的同學發現了一個好方法:
我們將所有的
的式子列出來,然後將它們都模
。
然後就會驚奇地發現下面的這張表:
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