信息学奥赛一本通 ybt 1933:【05NOIP普及组】循环 | 洛谷 P1050 [NOIP2005 普及组] 循环
【題目鏈接】
ybt 1933:【05NOIP普及組】循環
洛谷 P1050 [NOIP2005 普及組] 循環
【題目考點】
1.高精度
2.數學
【解題思路】
要求最后k位的循環長度,可以從低位向高位看。
假設n的最后k位為dkdk?1...d2d1d_kd_{k-1}...d_2d_1dk?dk?1?...d2?d1?
- 先看最低位d1d_1d1?,假設d1l1d_1^{l_1}d1l1??的最后一位位是d1d_1d1?,它的循環長度為l1l_1l1?
- 然后看倒數兩位d2d1d_2d_1d2?d1?,以(d2d1)l1(d_2d_1)^{l_1}(d2?d1?)l1?為單位進行累乘,可以保證結果的個位是d1d_1d1?,假設累乘l2l_2l2?次,(d2d1)l1l2(d_2d_1)^{l_1l_2}(d2?d1?)l1?l2?的倒數第2位是d2d_2d2?,即末兩位是d2d1d_2d_1d2?d1?,循環長度為l1l2l_1l_2l1?l2?
- 然后看倒數3位d3d2d1d_3d_2d_1d3?d2?d1?,以(d3d2d1)l1l2(d_3d_2d_1)^{l_1l_2}(d3?d2?d1?)l1?l2?為單位進行累乘,可以保證末兩位是d2d1d_2d_1d2?d1?,假設累乘l3l_3l3?次后(d3d2d1)l1l2l3(d_3d_2d_1)^{l_1l_2l_3}(d3?d2?d1?)l1?l2?l3?的倒數第3位是d3d_3d3?,即末3位是d3d2d1d_3d_2d_1d3?d2?d1?,那么循環長度為l1l2l3l_1l_2l_3l1?l2?l3?
以此類推,直到求出倒數k位的循環長度 - 如果累乘10次后,結果的末幾位與原數字仍然不同,那么就不會發生循環,輸出-1。
說明:
假設現在在看倒數m位,dm...d2d1d_m...d_2d_1dm?...d2?d1?,以(dm...d2d1)l1l2...lm?1(d_m...d_2d_1)^{l_1l_2...l_{m-1}}(dm?...d2?d1?)l1?l2?...lm?1?為單位進行累乘,可以保證最后m-1位為dm?1...d2d1d_{m-1}...d_2d_1dm?1?...d2?d1?。變化的只有結果的倒數第m位,該位置可能的數字只有0~9這10種。
- 如果每次乘(dm...d2d1)l1l2...lm?1(d_m...d_2d_1)^{l_1l_2...l_{m-1}}(dm?...d2?d1?)l1?l2?...lm?1?得到的倒數第m位都不同,那么10次累乘中,一定至少有一次其倒數第m位的值與dmd_mdm?相同。
- 如果10次累乘中,倒數第m位都沒能等于dmd_mdm?,說明這10次中,倒數第m位出現了重復的數字,倒數第m位數字的變化存在循環,而這個循環中沒有dmd_mdm?這個數字。所以dm...d2d1d_m...d_2d_1dm?...d2?d1?不會循環出現,應該輸出-1。
解法1:
嚴格按照上述方法執行,每次循環只看末m位,前先求累乘單位(dm...d2d1)l1l2...lm?1(d_m...d_2d_1)^{l_1l_2...l_{m-1}}(dm?...d2?d1?)l1?l2?...lm?1?,需要用到快速冪。
解法2:
整體思路與上述方法大體相同,不過直接取n的末k位作為累乘單位,不再分別取末1位,末2位。。。
- 先看最低位d1d_1d1?,假設(dkdk?1...d2d1)l1(d_kd_{k-1}...d_2d_1)^{l_1}(dk?dk?1?...d2?d1?)l1?的最后一位位是d1d_1d1?,它的循環長度為l1l_1l1?
- 然后看倒數兩位d2d1d_2d_1d2?d1?,以(dkdk?1...d2d1)l1(d_kd_{k-1}...d_2d_1)^{l_1}(dk?dk?1?...d2?d1?)l1?為單位進行累乘,可以保證結果的個位是d1d_1d1?,假設累乘l2l_2l2?次,(dkdk?1...d2d1)l1l2(d_kd_{k-1}...d_2d_1)^{l_1l_2}(dk?dk?1?...d2?d1?)l1?l2?的倒數第2位是d2d_2d2?,即末兩位是d2d1d_2d_1d2?d1?,循環長度為l1l2l_1l_2l1?l2?
- 然后看倒數3位d3d2d1d_3d_2d_1d3?d2?d1?,以(dkdk?1...d2d1)l1l2(d_kd_{k-1}...d_2d_1)^{l_1l_2}(dk?dk?1?...d2?d1?)l1?l2?為單位進行累乘,可以保證末兩位是d2d1d_2d_1d2?d1?,假設累乘l3l_3l3?次后(dkdk?1...d2d1)l1l2l3(d_kd_{k-1}...d_2d_1)^{l_1l_2l_3}(dk?dk?1?...d2?d1?)l1?l2?l3?的倒數第3位是d3d_3d3?,即末3位是d3d2d1d_3d_2d_1d3?d2?d1?,那么循環長度為l1l2l3l_1l_2l_3l1?l2?l3?
- 如此來做,上一次循環的累乘的結果可以直接作為下一次循環的累乘單位,可以省去解法1中做快速冪的過程。
【題解代碼】
代碼中幾個Multiply函數名字相同,參數個數或類型不同,構成函數重載,幾個函數都可以被正確使用。
解法1:
#include<bits/stdc++.h> using namespace std; #define N 305 void numcpy(int a[], int b[])//b拷貝給a {for(int i = 0; i <= b[0]; ++i)//把r賦值給a a[i] = b[i]; } void Multiply(int a[], int b[], int m)//a *= b 高精乘高精 結果只取末m位 {int r[N] = {}, ri;for(int i = 1; i <= a[0]; ++i){int c = 0;for(int j = 1; j <= b[0]; ++j){r[i+j-1] += a[i]*b[j] + c;c = r[i+j-1] / 10;r[i+j-1] %= 10; }r[i+b[0]] += c;}ri = a[0] + b[0];while(r[ri] == 0 && ri > 1)ri--;r[0] = ri;if(r[0] > m)r[0] = m; numcpy(a, r); } void Multiply(int a[], int b)//a *= b 高精乘低精 {int c = 0, i;for(i = 1; i <= a[0]; ++i){a[i] = a[i]*b + c;c = a[i] / 10;a[i] %= 10; }while(c > 0){a[i] = c % 10;c /= 10;i++;}while(a[i] == 0 && i > 1)i--;a[0] = i; } void Divide(int a[], int b) //高精除低精 a/=b {int x = 0, ai;//余數 for(int i = a[0]; i >= 1; i--){x = x * 10 + a[i];a[i] = x / b;x %= b;}ai = a[0];while(a[ai] == 0 && ai > 1)ai--;a[0] = ai; } void fastPower(int a[], int b[], int m)//快速冪 a^b取末m位 ,結果保存給a {int r[N] = {1, 1}, c[N];//a為基數,c為指數,r為結果 numcpy(c, b);while(!(c[0] == 1 && c[1] == 0))//c != 0{if(c[1] % 2 == 1)//用b的個位判斷其是否是偶數 Multiply(r, a, m); Multiply(a, a, m);Divide(c, 2);}numcpy(a, r); } void tonum(int a[], char s[])//字符串轉為數字數組 {int len = strlen(s);for(int i = 1; i <= len; ++i)a[i] = s[len-i] - '0';a[0] = len; } void cutNum(int a[], int n, int b[])//截取數字a的后n位 賦值給數字b {for(int i = 1; i <= n; ++i)b[i] = a[i];b[0] = n; } int main() {int n[N] = {}, a[N] = {}, b[N] = {}, l[N] = {1,1}, k;//a:累乘單位 l:循環長度 初值為1 char s[N];cin >> s >> k;tonum(n, s);for(int i = 1; i <= k; ++i)//i:看后i位 {cutNum(n, i, a);//構造a為n的后i位 fastPower(a, l, i);//n的后i位累乘l次,使a成為累乘單位 cutNum(n, i, b);//構造b為n的后i位 bool isFound = false; //第i位的循環長度是否找到 for(int j = 1; j <= 10; ++j){Multiply(b, a, i);if(b[i] == n[i])//如果倒數第i位又變回為原來的倒數第i位 {Multiply(l, j);//找到第i位的循環長度為j isFound = true;break;}}if(isFound == false){//如果沒找到,則不存在循環長度 cout << -1;return 0;}}for(int i = l[0]; i >= 1; i--)//輸出循環長度cout << l[i]; return 0; }解法2:
#include<bits/stdc++.h> using namespace std; #define N 305 int k;//結果取末k位 void numcpy(int a[], int b[])//b拷貝給a {for(int i = 0; i <= b[0]; ++i)//把r賦值給a a[i] = b[i]; } void Multiply(int a[], int b[], int r[])//a * b = r 高精乘高精 結果只取末k位 {for(int i = 1; i <= a[0]; ++i){int c = 0;for(int j = 1; j <= b[0]; ++j){r[i+j-1] += a[i]*b[j] + c;c = r[i+j-1] / 10;r[i+j-1] %= 10; }r[i+b[0]] += c;}int ri = a[0] + b[0];while(r[ri] == 0 && ri > 1)ri--;r[0] = ri;if(r[0] > k)r[0] = k; } void Multiply(int a[], int b[])//a *= b 高精乘高精 結果只取末k位 {int r[N] = {};Multiply(a, b, r);numcpy(a, r); } void Multiply(int a[], int b)//a *= b 高精乘低精 {int c = 0, i;for(i = 1; i <= a[0]; ++i){a[i] = a[i]*b + c;c = a[i] / 10;a[i] %= 10; }while(c > 0){a[i] = c % 10;c /= 10;i++;}while(a[i] == 0 && i > 1)i--;a[0] = i; } void tonum(int a[], char s[])//字符串轉為數字數組 {int len = strlen(s);for(int i = 1; i <= len; ++i)a[i] = s[len-i] - '0';a[0] = len; } int main() {int n[N] = {}, a[N] = {}, b[N] = {}, c[N] = {}, l[N] = {1,1};//a:累乘單位 l:循環長度 初值為1 char s[N];cin >> s >> k;tonum(n, s);numcpy(a, n);//累乘單位a初值為nfor(int i = 1; i <= k; ++i)//i:看后i位 {bool isFound = false; //第i位的循環長度是否找到 c[0] = c[1] = 1;//c = 1; 累乘乘積 for(int j = 1; j <= 10; ++j){Multiply(c, a);//高精乘高精 c *= a;memset(b, 0, sizeof(b));Multiply(c, n, b);//高精乘高精 b = a*nif(b[i] == n[i])//如果倒數第i位又變回為原來的倒數第i位 {Multiply(l, j);//高精乘低精 找到第i位的循環長度為j isFound = true;break;}}if(isFound == false){//如果沒找到,則不存在循環長度 cout << -1;return 0;}numcpy(a, c);//使當前c的值為下一次的累乘單位a了}for(int i = l[0]; i >= 1; i--)//輸出循環長度cout << l[i]; return 0; }總結
以上是生活随笔為你收集整理的信息学奥赛一本通 ybt 1933:【05NOIP普及组】循环 | 洛谷 P1050 [NOIP2005 普及组] 循环的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我的世界基岩版json_(1.8.0.1
- 下一篇: 信息学奥赛一本通 2050:【例5.20