uva11361(數論DP)

題意:


基本是看了大白上的思路,然後還參考了http://blog.csdn.net/lenleaves/article/details/9104417

統計大於等於a,小於等於b,每一位之和可以被k整除,且本身也能被k整除的數的數量。

用f(d,m1,m2)表示剩餘d個數字,這d個數字之和模k爲m1,這d個數字組成的整數倍k整除之後餘m2, 這樣的數字的數量,

求a與b之間這樣數字的數量,我們轉化問題爲前綴和只差即 sum(b)-sum(a-1)  (具體求解過程的不同,此處可能會有小出入)。

狀態方程 訓練指南上面有一個小錯誤。

正確的狀態方程應爲

f(d,m1,m2)=sum{ f( d-1,(m1-x) mod k,(m2-x*10^(d-1)) mod k ) |x=0,1,2.......9}

注意當我們將星號限制在後面三位的時候,我第四位的枚舉範圍,應在原本第四位的範圍之內。而之後的枚舉過程中,由於第四位小於原本的第四位,所以低位可以從0枚舉到9


#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>

using namespace std;

int a,b,k;
int dp[15][110][110];
int pw[11]={1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };

int dfs(int d,int m1,int m2) {
    if(d == 0)
	   	return (m1 == 0 && m2 == 0) ? 1 : 0;
    if(dp[d][m1][m2] >= 0)
	   	return dp[d][m1][m2];
    dp[d][m1][m2]=0;
    for(int i = 0; i < 10; i++)
        dp[d][m1][m2] += dfs(d - 1, ((m1 - i) % k + k) % k, ((m2 - i * pw[d-1]) % k + k) % k);
    return dp[d][m1][m2];
}

int func(int n) {
    int d = 0, m1 = 0, m2 = 0, a[15];
    if(n ==0)
	   	a[d++] = 0;
    while(n != 0) {
        a[d] = n % 10;
        n /= 10;
        d++;
    }
    int ans=0;
    for(int i = d - 1; i >= 0; i--) {
        if(i!=0)
        for(int j=0;j<a[i];j++) {
            ans += dfs(i,(k-(m1+j)%k)%k,(k-(m2+pw[i]*j)%k)%k);
        }
        else
        for(int j = 0; j <=a[i]; j++) {
            ans+=dfs(i,(k-(m1+j)%k)%k,(k-(m2+pw[i]*j)%k)%k);
        }
        m1 += a[i];
        m2 += pw[i] * a[i];

    }
    return ans;
}

int main() {
    int t;
	scanf("%d", &t);
    while(t--) {
		scanf("%d%d%d", &a, &b, &k);
        if(k > 85)
            printf("0\n");
        else {
            memset(dp,-1,sizeof(dp));
            printf("%d\n",func(b)-func(a-1));
        }
    }
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章