題目描述:
單身!依然單身!
吉哥依然單身!
ds 級碼農吉哥依然單身!
所以,他平生最恨情人節,不管是 214 還是 77,他都討厭!
吉哥觀察了 214 和 77 這兩個數,發現:
2+1+4=7
7+7=7×2
77=7×11
最終,他發現原來這一切歸根到底都是因為和 7 有關!
所以,他現在甚至討厭一切和 7 有關的數!
什麼樣的數和 7 有關呢?
如果乙個整數符合下面三個條件之一,那麼我們就說這個整數和 7 有關:
整數中某一位是 7;
整數的每一位加起來的和是 7 的整數倍;
這個整數是 7 的整數倍。
現在問題來了:吉哥想知道在一定區間內和 7 無關的整數的平方和。
輸入格式
第一行包含整數 t,表示共有 t 組測試資料。
每組資料佔一行,包含兩個整數 l 和 r。
輸出格式
對於每組資料,請計算 [l,r]中和 7 無關的數字的平方和,並將結果對 10^9+7取模後輸出。
資料範圍
1≤t≤50,
1≤l≤r≤10^18
輸入樣例:
3
1 910 11
17 17
輸出樣例:
236
2210
分析:
本題是比較難的數字dp問題,思路並不複雜,只是**較為繁瑣,因為數字是long long型別才能存的下的,所以在計算一連串乘法時需要不停的取模,比如long long a,b;計算a * b就應該寫成(a % p) * (b % p) % p,其中p = 1e9 + 7,另外對於int型別的陣列,如果計算的中間結果超過了int,需要及時的強轉為long long,忽視其中任何一步可能爆int的乘法運算,都會引起結果的錯誤。下面正式分析本題的解法。
直接dfs的話思路比較簡潔,但是不方便進行記憶化搜尋,需要儲存的東西太多了,而不進行記憶化搜尋顯然會超時,所以還是採用動態規劃的解法。一般數字dp的題目用f[i][j]表示開頭是j的i位數的滿足條件的數字的個數,比如不含7的i位數的個數就很容易用f[i][j]表示出來,但是題目新增了兩個條件,這個數不能被7整除,這個數的每一位之和不能被7整除,所以對整數的劃分應該劃分為這個數模上7的餘數以及數的各位之和模上7的餘數這兩個等價類,因此要對f陣列增加兩維。f[i][j][a][b]表示乙個i為數最高位是j,各位之和模上7等於a,這個數模上7等於b,注意這裡還沒有給出f陣列的含義,因為如果只是統計這樣數的個數,那麼本題就相當簡單了,但是題目要求的是與7無關整數的平方和,這就相當複雜了,如果我們算到最後一位才去加上這個數的平方和,超時是肯定的,我們需要求的是i位數中以j開頭的滿足條件的整數的平方和是多少,需要有明確的遞推式。下面開始推公式:
設滿足條件的以j開頭的i位數有jx1,jx2,...,jxn。其中xi表示j後面的i-1位數,我們要求這些數的平方和s,也就是s = (jx1)^2 + ... + (jxn)^2。又jxi = j * 10^(i-1) + xi,故s = (j*10^(i-1))^2 + 2 * j * 10^(i-1) * x1+ x1^2) + ... + (j*10^(i-1))^2 + 2 * j * 10^(i-1) * xn + xn^2) = n * j*10^(i-1))^2 + 2 * j * 10^(i-1) * (x1+...+xn) + (x1^2 +...+ xn^2),觀察表示式**現的項,分別是滿足條件數的個數n,這些數中後n-1位組成數字的和x1 + ... + xn,以及這些數後n-1位組成數字的平方和。所以f陣列要儲存的是以j開頭滿足條件的i位數的個數、和以及平方和,可以使用結構體儲存,s0表示個數,s1表示和,s2表示平方和。下面要考慮的是,如何通過f[i-1][k][c][d]的s0,s1,s2推出f[i][j][a][b]的這些資訊,首先考慮c和d的取值,i位數的各位之和是a,則前i-1位的和是a - j,故c = a - j,當然還要對7取模。又前i-1位數j * 10^(i-1) + d對7取模結果是b,所以d = b - j * 10^(i-1)再對7取模。令v1 = f[i][j][a][b],v2 = f[i-1][k][c][d]。顯然v1.s0 += v2.s0。那麼如何通過前i-1位數組成數字的和求出加上最高位j組成數字的和呢?jx1 + jx2 + ...+jxn = (j*10^(i-1) + x1)+...+(j*10^(i-1)+xn) = n * j * 10^(i-1) + (x1 + ... + xn)即v1.s1 += v2.s0 * j * 10^(i-1) + v2.s1,最後根據本段開頭推出的公式得v1.s2 += v2.s0 * j*10^(i-1))^2 + 2 * j * 10^(i-1) * v2.s1 + v2.s2,當然**在實行這部分時每步乘法的後面都需要加上取模運算,表示式會更加複雜。
現在我們知道了以j開頭的i位數的與7無關的數的平方和是怎麼有前面的j和後面的x推出來了,問題就解決了一大半,剩下的就是自高向低逐位列舉n的每一位了,需要儲存以及列舉的位數的數字之和last1,以及已經列舉的數組成的數字last2。我們要求f[n][j][a][b],其中a與b都不能是0,我們不妨先求出能夠使a和b是0的數是哪些,然後排除這些數即可。要想前面的數字last1與後面的數字組合起來的數與7無關,則需要求出f[i+1][j][c][d]中的c和d是多少時,(c + last1) % 7 == 0,即c = mod(-last1,7),並且(last2 * 10^(i+1) + d) % 7 == 0,d = mod(-last2 * 10^(i+1),7),(這裡為什麼是乘以10的i+1字方?是因為列舉第i位時last2儲存的還是i+1前面的數)。我們知道了c和d取這些值時會使得最終的數與7有關,那麼我們列舉所有的c和d不為該值時的數,將題目的平方和加起來就是我們需要求的結果了。
ps:可能即使看上面那麼詳細的推導過程還是一頭霧水,看不懂的地方看**時回過頭來看分析過程就可以看懂了。本題就是乙個取模麻煩,公式推導出來實現還是很簡單的。
#include #include #include using namespace std;
const int n = 20,p = 1e9 + 7;
typedef long long ll;
int p7[n],p9[n];
struct ff[n][10][7][7];
int mod(ll x,int y)
void init()
ll p = 10;
for(int i = 2;i < n;i++,p *= 10)}}
}}
p7[0] = p9[0] = 1;//預處理10的若干字方對7和p取模的結果,打表備查
for(int i = 1;i < n;i++)
}f getf(int x,int y,int a,int b)
}return ;
}int get(ll n)
if(x == 7) break;
last1 += x,last2 = last2 * 10 + x;
if(!i && last1 % 7 && m % 7) res = (res + m % p * (m % p)) % p;
}return res;
}int main()
return 0;
}
恨 7 不成妻
求出一段區間內與 7 無關的數的平方和,我們定義這個數與 7 有關當且僅當這個數滿足下列條件之一 1 某一位為 7 2 數字和為 7 的倍數 3 這個數本身是 7 的倍數。這題並不算裸的數字 dp 題,顯然如果對於計數我們很容易得到這個區間內滿足條件的個數,而為了使數字 dp 的 dp 能夠有子狀態...
數字dp(恨7不成妻)
hdu 4507 吉哥系列故事 恨7不成妻 數字dp 思路 想必普通的統計滿足條件的個數都會吧,這裡就不在贅述了,dp i j k 代表長度為i,數字對7取餘數為j,數字各個位數加起來對7取餘數k 僅僅用dp i j k 的值代表個數是不能得到答案的,還要統計滿足條件的和還有平方和 開結構體,維護和...
數字dp 恨7不成妻
題目描述 單身!依然單身!吉哥依然單身!ds 級碼農吉哥依然單身!所以,他平生最恨情人節,不管是 214 還是 77 他都討厭!吉哥觀察了 214 和 77 這兩個數,發現 2 1 4 7 7 7 7 2 77 7 11 最終,他發現原來這一切歸根到底都是因為和 7 有關!所以,他現在甚至討厭一切和...