網教10. 琪露諾的完美算數教室——⑨的統計I

衆所周知,琪露諾(チルノ,Cirno)是幻想郷 (げんそうきょう)中首屈一指的天才,可以說⑨就是她的代名詞。

然而如今,她遇到了一個和⑨有關的難題。你能幫助她麼?

題目是這樣的,給出兩個數 a 和 b (0 <= a <= b <= 10^10000),求 a 到 b 之間(包括a和b)的數字中,有多少個數字是包含9的(例如 19,910 等都是包含9的數字)。

輸入

第一行爲一個數字 T (0 < T <= 100) 表示數據組數。
之後的 T 行,每行包含兩個數 a 和 b (0 <= a <= b <= 10^10000)。

輸出

對每組數據輸入,輸出一個數字,表示 a 到 b 之間的數字中(包括a和b),有多少個數字是包含9的。(注意:答案可能很大)



測試輸入關於“測試輸入”的幫助 期待的輸出關於“期待的輸出”的幫助 時間限制關於“時間限制”的幫助 內存限制關於“內存限制”的幫助 額外進程關於“{$a} 個額外進程”的幫助
測試用例 1 以文本方式顯示
  1. 1↵
  2. 0 9↵
以文本方式顯示
  1. 1↵
1秒 64M 0
題解:

感覺這個題挺難的,題目寫的很簡單,但是思考量非常大。看了好幾個博客問了幾個人終於才弄明白。

首先要知道:從0到10^n一共有10^n-9^n個含9的數,在遇到9之前就一直這樣算,遇到9之後就直接加上9之後的那些數即可。

比如13012958就是求10000000的9+3000000的9+10000的9+2000的9+900的9+58(900到957,一共是58個數)

也就是先從前往後走,在遇到9之前算(10^n-9^n)*a,遇到9之後直接加(也就還是a*10^n,就不用減9^n了)

由於數據很大,所以用很多個int數組來存,最後逐一輸出,除了第一個之外其他的數組在輸出的時候都要補前綴0.數據有10^10000,而int只存10^8,所以開1300個int數組就夠儲存的了。

還有要注意的是大數相加相減和相乘,這裏要記得取模進位。

AC代碼:

#include<stdio.h>  
#include<string.h>  
#define mod 100000000  
//因爲int是有精度範圍的,所以加一個mod以防止爆int,最後輸出的時候再逐個輸出  
#define last 4000  
//last是假定的答案最後一位,一開始開太大了就T了 TAT  
char start[10005], end[10005];  
int res1[4005], res2[4005], res[4005];  
  
void add(int a[], int ak)  
{  
    int i;  
    a[last] += ak;  
    for (i = last; i >= 0; i--)  
    {  
        if (a[i] >= mod)  
        {  
            a[i] = a[i]-mod;  
            a[i - 1]++;  
        }//進位  
        else break;  
    }  
}  
void multi(int a[], int ak)  
{  
  
    int i, k;  
    int j = 0;  
    while (!a[j])  
        j++;//找到非0的第一位  
    for (i = last; i >= j; i--)  
        a[i] = a[i]*ak;  
    k = 0;  
    for (i = last; i >= j - 2; i--)  
    {  
        a[i] += k;  
        k = a[i] / mod;  
        a[i] = a[i] % mod;  
    }//進位  
}  
void minus(int a1[], int a2[], int a3[])  
{  
    int i;  
    for (i = last; i >= 0; i--)  
    {  
        a1[i] = a2[i] - a3[i];  
    }  
    for (i = last; i >0; i--)  
    {  
        if (a1[i] < 0)  
        {  
            a1[i - 1] --;  
            a1[i] += mod;  
        }  
    }  
}//a1是結果數組  
  
int ans1[10005] = { 0 }, ans2[10005];//分別儲存乘10和9  
void fun(char a[],int b)  
{  
    memset(ans1, 0, sizeof(ans1));  
    memset(ans2, 0, sizeof(ans2));  
    int i;  
    int c[10005];  
    for (i = 0; i < strlen(a); i++)  
        c[i] = a[i] - '0';  
    ans1[last] = c[0];//給第一個元素賦初值  
    ans2[last] = c[0];  
    int flag = 0;  
    if (c[0] == 9)  
        flag = 1;  
    for (i = 1; i < strlen(a); i++)  
    {  
        multi(ans1, 10);  
        add(ans1, c[i]);//每次都把ans1乘10再把a加到ans1裏  
        multi(ans2, 9);//把ans2乘9(即求9^n)  
        if (!flag)  
            add(ans2, c[i]);//如果前面的數裏面有9,就不用再加了  
        if (c[i] == 9)  
            flag = 1;  
    }  
    if (b == 1)  
        minus(res1, ans1, ans2);//最後再把它們減一下  
    else  
        minus(res2, ans1, ans2);  
}  
  
int main()  
{  
    int t;  
    scanf("%d", &t);  
    while (t--)  
    {  
        scanf("%s%s", start, end);  
        int len1, len2;  
        len1 = strlen(start);  
        len2 = strlen(end);  
        fun(start, 1);  
        fun(end, 2);  
        minus(res, res2, res1);  
        int i;  
        for (i = 0; i < len2;i++)  
        if (end[i] == '9')  
        {  
            res[last]++;//說明最後一個沒有算上  
            break;  
        }  
        i = last;  
        while (res[i] >= mod)  
        {  
            res[i - 1]++;  
            res[i] -= mod;  
            i--;  
        }  
        i = 0;  
        while (!res[i]&&i<=last)  
            i++;//把前綴的0全都跳過去  
        printf("%d", res[i]);//第一個不用補0  
        i++;  
        while (i <= last)  
        {  
            printf("%08d", res[i]);//res數組中不足8位的就補0  
            i++;  
        }  
        printf("\n");  
    }  
    return 0;  
}  


測試輸入關於“測試輸入”的幫助 期待的輸出關於“期待的輸出”的幫助 時間限制關於“時間限制”的幫助 內存限制關於“內存限制”的幫助 額外進程關於“{$a} 個額外進程”的幫助
測試用例 1 以文本方式顯示
  1. 1↵
  2. 0 9↵
以文本方式顯示
  1. 1↵
1秒 64M 0
發佈了25 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章