問題描述
小明先把硬幣擺成了乙個
n 行 m 列的矩陣
。隨後,小明
對每乙個硬幣分別進行一次 q 操作
。對第x行第y列的硬幣進行 q 操作的定義:
將所有第 i*x 行,第 j*y 列的硬幣進行翻轉
。其中i和j為
任意使操作可行的
正整數,行號和列號都是從1開始。
// i、j為任意數
當小明對所有硬幣都進行了一次 q 操作後,他發現了乙個奇蹟——所有硬幣均為正面朝上。
小明想知道最開始有多少枚硬幣是反面朝上的。於是,他向他的好朋友小m尋求幫助。
聰明的小m告訴小明,只需要對所有硬幣再進行一次q操作,即可恢復到最開始的狀態。然而小明很懶,不願意照做。於是小明希望你給出他更好的方法。幫他計算出答案。
輸入格式
輸入資料報含一行,兩個正整數 n m,含義見題目描述。
輸出格式
輸出乙個正整數,表示最開始有多少枚硬幣是反面朝上的。
樣例輸入
2 3樣例輸出
1資料規模和約定
對於10%的資料,n、m <= 10^3;
對於20%的資料,n、m <= 10^7;
對於40%的資料,n、m <= 10^15;
對於10%的資料,n、m <= 10^1000(10的1000次方)。思路
1. 如果一枚硬幣被翻了奇數次,那麼它原來的狀態肯定是反面朝上,所以,我們要找的就是
被翻了奇數次的硬幣(立腳點)
2. q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。
正向看可能不好想,那麼我們反向看一下,假設乙個橫座標為x的硬幣,在翻哪些硬幣的時候會翻到它呢?
其實就是這個數x所有的約數,比如橫座標為4的硬幣,那麼,在翻橫座標為1,2,4的硬幣時都會翻到它,縱座標的情況是一樣的。
3.對於乙個硬幣,我們必須同時考慮其橫座標x和縱座標y,假如橫座標被翻了a次,縱座標被翻了b次,則這個硬幣總共被翻了
a*b次,若想要這個硬幣被翻奇數次,a和b必須都得是奇數,即x和y都有奇數個約數
4.那麼問題來了:哪些數有奇數個約數呢?不管你知不知道,反正現在你知道了,
完全平方數有奇數個約數。那麼什麼又是完全平方數呢,簡單的說就是n^2,n為自然數,也就是0,2,4,9……
(平方根只有乙個數,其他約數都是成對的)
5.問題又來了,怎麼求完全平方數的個數呢,首先,我們已經知道了這個矩陣式n*m的,而且是從1開始編號的,對於n,我們可以求sqrt(n),然後取整,容易想出,在1-n的範圍內的
完全平方數的個數為(int)(sqrt(n))個,而sqrt(n)*sqrt(m)就是所有的橫縱座標都是完全平方數的硬幣的個數。
(完全平方數:1*1、2*2、……,舉例:15,位於3*3和4*4之間,開方取整就能夠得到3)
6.下面,我們迎來了終極問題,題目思路有了,但是超大規模的資料問題並沒有得到解決,反而更麻煩了,因為居然還搞出了個開方的操作,但很不幸,這就是一道悲催的大數高精度題,而且還得自己寫出大數相乘,大數開方,大數比較等操作
首先,讓我們明確一下思路,到底如何實現乘法和開方,對於大數的儲存,我們使用string,因為它的長度比較容易控制
大數運算:
乘法:其實有很多速度快而且更巧妙的大數乘法,但是在藍橋杯,我們只要使用
模擬手演算法應該是夠用,雖說是模擬,但也有一些我們常用但不太了解的規律,我們在手算乘法的時候,需要進行
移位和進製,這兩個操作我們會通過兩步完成
1.移位:對於兩個數a=12,b=25,在相乘的時候我們讓每一位數分別相乘,即a[0]*b[0]=2 , a[0]*b[1]=5 , a[1]*b[0]=4 , a[1]*b[1]=10,那麼規律就是,對於兩個數a[i] , b[j],所有
i+j相同的數(i+j相同的數單位相同)都要加到一起,所以我們要把5+4=9合併為乙個數,也就是說,將每一位數相乘之後,我們實際得到了2,9,10三個沒有進製的數
2.進製:通過上面的操作,我們的2,9,10三個沒有進製的數,下面就要進行進製,因為我們是把高位相乘得到的數放在前面,低位相乘得到的數放在後面,所以這三個數排列自然也是高位在前,地位在後,所以要從右向左進製,進製的方法就是a[i+1]=a[i+1]+a[i]/10 , a[i]=a[i]%10,如果放到這三個數上面,就是,9+10/10=10,然後10%10=0,這三個數變成2,10,0,再一次就是2+10/10=3,10%10=0,三個數變為3,0,0,而這正是我們要求的結果,在實際操作中,我們習慣於將數
倒著存放,即將數存為10,9,2,這是
為了進製方便,因為如果正序的話如果最高位發生進製我們就要將每乙個數向後移動一位從而挪出乙個空位,想想都麻煩,倒序的話因為是向後進製,想怎麼進就怎麼進。所以剛才的公式改為
a[i]=a[i]+a[i+1]/10(高位=高位+低位/10 低位除10得到進製數) , a[i+1]=a[i+1]%10(低位=低位%10 低位餘10得到低位數)
開方:這個開方方法不是我想出來的,是參照了網上的乙個方法,我感覺挺好。實際上也可以用牛頓逼近法。
這個方法的前提:假如乙個數有n位,若n為
偶數,那麼這個數的
方根有n/2位;若
n為奇數,那麼方根為
(n+1)/2位。
然後,讓我們實際看乙個例子,我們假設這個數就是1200
1.很明顯,它有4位,所以它的方根有2位,然後,我們通過下面的方法來列舉出它的整數根
00*00=0<1200
10*10=100<1200
20*20=400<1200
30*30=900<1200
40*40=1600>1200
所以,這個根的十位就是3,然後,再列舉個位
31*31=961<1200
32*32=1024<1200
33*33=1089<1200
34*34=1156<1200
35*35=1225>1200
所以,這個根就是34,因為
平方增長的速度還是比較快的,所以速度沒有太大問題,這裡有乙個地方需要處理一下,如果乙個數很長,那麼它的方根的位數也比較長,在列舉高位的時候,我們沒有必要把後面的0都加上,因為那樣會影響速度,其實0的個數完全可以算出來,然後將0的個數一起送入比較函式比較即可,這樣可以提高速度
比較:上面我們說過,比較函式接受的兩個數只有乙個是完整的數,另外乙個實際上是
高幾位的平方和0的個數,但處理方式差不多,都是
先比較位數,位數大數就大,位數一樣就逐位比較,只要別忘了比較那一堆0就好了,最後其實不用把情況分的太清楚,只要返回0,1就行,因為只有當乙個數大於n的時候才對我們有意義 **
#include
#include
#include
usingnamespacestd;
string strmul(string a,string b)
;for(i=0;ifor(j=0;j
//for(i=0;i<5;i++)
//cout<
//cout<
for(i=0;i
//for(i=0;i<5;i++)
//cout<
for(i=len1+len2-1;i>=0;i--)
for(;i>=0;i--)
returnresult;
}intstrcmp(string a,string b,intpos)
} } string strsqrt(string a)
result[i]--;
}returnresult;
}intmain()
歷屆試題 矩陣翻硬幣 大數開方
思路 假定乙個硬幣初始時是正面,它被翻轉偶數次仍然是正面,翻轉奇數次就是反面。考慮位於座標 x,y 的隱蔽,設a是x的約數,b是y的約數,那麼翻轉座標為 a,b 的硬幣時,x,y 也會翻轉,我們只關心 x,y 翻轉次數的奇偶性。那麼 x,y 的翻轉次數就是x的約數個數 y的約數個數,題目要求計算反面...
藍橋杯 歷屆試題 矩陣翻硬幣(大數)
問題描述 小明先把硬幣擺成了乙個 n 行 m 列的矩陣。隨後,小明對每乙個硬幣分別進行一次 q 操作。對第x行第y列的硬幣進行 q 操作的定義 將所有第 i x 行,第 j y 列的硬幣進行翻轉。其中i和j為任意使操作可行的正整數,行號和列號都是從1開始。當小明對所有硬幣都進行了一次 q 操作後,他...
藍橋杯 PREV 34 矩陣翻硬幣(大數)
prev 34 矩陣翻硬幣 思路 1.由題意可知,將所有硬幣都進行一次q操作後,被翻轉奇數次的硬幣是反面朝上的 2.定義f x f x f x 為正整數x xx的約數個數,那麼座標為 a,b a,b a,b 的硬幣會被翻轉f a f b f a f b f a f b 次,我們知道只有奇數乘以奇數結...