HDU 4507 求指定範圍內與7不沾邊的所有數的平方和 (數位DP)
題意:
求區間[l,r]內所有與7無關的數的平方和(取模)
定義與7無關的數:
1.數字的數位上不能有7
2.數字的數位和不能是7的倍數
3.數字本身不能是7的倍數
分析:
狀態的儲存:
1.數位上不能有7: 只需列舉數位的數字的時候跳過7就好 if (i == 7) continue;
2.數位和不能是7的倍數: 那麼開一維儲存數位和除以7的餘數
3.數字本身不能是7的倍數:再開一維儲存數字除以7的餘數
綜上,dp[i][j][k]3個維度儲存的數字屬性分別是:
i : 當前處理的數位
j : 數位和%7 等於j
k: 數字本身%7等於k
對於具有上述屬性的數,用dp儲存它們的3個值:(用結構體)
cnt: 具有該屬性的所有數字的個數
s :具有該屬性的所有數字的和
ss:具有該屬性的所有數字的平方和
為什麼要儲存這3個值?為了下面的計算
狀態的轉移:
關於狀態轉移,先簡單的寫這樣一個式子:dp[i][j][k] = ∑dp[i-1][(j+dig)%7][(k*10+dig)%7](這裡的求和符號不指加法,是一個抽象的意義)
其中dig是列舉的正在處理的數位i上所有可能的數字(這個式子只能幫助理解狀態是如何轉移的但是卻不表示具體的運算,dp是結構體當然不能直接運算)
上面的等式,我們稱等式左邊表示總狀態,等式右邊為其子狀態,顯然總狀態是等於所有子狀態的“總和”(我說的狀態的總和並非指加法運算)
那麼怎麼通過子狀態算出總狀態呢?
具體的計算:
先說幾句廢話:
對於1234這個數,它的數位上的數是1,2,3,4,它的數位和是1+2+3+4,它自身的數值是 1*1000+2*100+3*10+4
如果我知道數字234是與7無關的數,在其前面加一個1,也是與7無關的數,我是怎樣計算在其前面加一個1之後的數的平方和的呢
(1*1000)^2 + 2*(1*1000)*234 + 234^2 這裡相當於(1000+234)^2
注意:在具體的狀態轉移中,我們是不知道234這個值,我們只知道有這麼一個子狀態
另外,這只是一個數的平方,我們要求的是所有滿足的數的平方和,所以最後具體的算式如下:
設總狀態為ans,它其中一個子狀態為tmp,列舉正在處理的這一數位上的數字為 i ,數位 i 在整個數字中具體的數值是i*10^p
那麼有: //這裡自己手算寫寫就會懂了,注意這裡都是+=,都是加上這位數後面的個數跟狀態。
(1) ans.cnt += tmp.cnt
(2) ans.s += tmp.s + [ i*10^p ]*tmp.cnt //之所以*數量,是因為後面有許多個符合條件的數,這一位可以跟後面所有符合條件的數搭配
(3) ans.ss += tmp.ss + 2*(i*10^p)*tmp.s + [(i*10^p)^2]*tmp.cnt //這裡的中間部分兩倍那裡,比較坑。。。
之所以變態是因為這個數太大了,不懂不懂就爆long long 所以哪裡都要%Mod,少一個Mod就wa,另外這題比較新,返回的是結構體,判斷記憶化的時候,也是用結構體裡的變數實現的,通過建構函式初始化
總結題目是個好習慣: 完全沒有想到原來是可以用結構體dfs , 而且考慮的也不全面 ,雖然之前的打法粗糙的想法是過了案例 , 但仔細想想又不行 , 還是太年輕了
/* * 如果一個整數符合下面3個條件之一,那麼我們就說這個整數和7有關—— 1、整數中某一位是7; 2、整數的每一位加起來的和是7的整數倍; 3、這個整數是7的整數倍; 求一個區間中與7無關的數的平方和 */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const long long MOD=1000000007LL; struct Node { long long cnt;//與7無關的數的個數 long long sum;//與7無關的數的和 long long sqsum;//平方和 }dp[20][10][10];//分別是處理的數位、數字和%7,數%7 int bit[20]; long long p[20];//p[i]=10^i Node dfs(int pos,int pre1,int pre2,bool flag) { if(pos==-1) { Node tmp; tmp.cnt=(pre1!=0 && pre2!=0); tmp.sum=tmp.sqsum=0; return tmp; } if(!flag && dp[pos][pre1][pre2].cnt!=-1) return dp[pos][pre1][pre2]; int end=flag?bit[pos]:9; Node ans; Node tmp; ans.cnt=ans.sqsum=ans.sum=0; for(int i=0;i<=end;i++) { if(i==7)continue; tmp=dfs(pos-1,(pre1+i)%7,(pre2*10+i)%7,flag&&i==end); ans.cnt+=tmp.cnt; ans.cnt%=MOD; ans.sum+=(tmp.sum+ ((p[pos]*i)%MOD)*tmp.cnt%MOD )%MOD; ans.sum%=MOD; ans.sqsum+=(tmp.sqsum + ( (2*p[pos]*i)%MOD )*tmp.sum)%MOD; ans.sqsum%=MOD; ans.sqsum+=( (tmp.cnt*p[pos])%MOD*p[pos]%MOD*i*i%MOD ); ans.sqsum%=MOD; } if(!flag)dp[pos][pre1][pre2]=ans; return ans; } long long calc(long long n) { int pos=0; while(n) { bit[pos++]=n%10; n/=10; } return dfs(pos-1,0,0,1).sqsum; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; long long l,r; p[0]=1; for(int i=1;i<20;i++) p[i]=(p[i-1]*10)%MOD; for(int i=0;i<20;i++) for(int j=0;j<10;j++) for(int k=0;k<10;k++) dp[i][j][k].cnt=-1; scanf("%d",&T); while(T--) { scanf("%I64d%I64d",&l,&r); long long ans=calc(r); ans-=calc(l-1); ans=(ans%MOD+MOD)%MOD; printf("%I64d\n",ans); } return 0; }View Code