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