//全網最詳la細ji解,附本人三次提交記錄
題目描述
作爲體育委員,C君負責這次運動會儀仗隊的訓練。儀仗隊是由學生組成的N * N的方陣,爲了保證隊伍在行進中整齊劃一,C君會跟在儀仗隊的左後方,根據其視線所及的學生人數來判斷隊伍是否整齊(如下圖)。 現在,C君希望你告訴他隊伍整齊時能看到的學生人數。
輸入輸出格式
輸入格式:
共一個數N
輸出格式:
共一個數,即C君應看到的學生人數。
輸入輸出樣例
輸入樣例#1:
4
輸出樣例#1:
9
說明
【數據規模和約定】
對於 100% 的數據,1 ≤ N ≤ 40000
分析及思路詳解
這看上去是一道簡單的數學問題,從理論上說,判斷遮擋視線,我們只需要利用一個東西,叫相似三角形(小學操作啊),然後根據相似三角形的一些性質,我們可以知道x軸,y軸,和視線圍成的三角形,只要邊(這個點的橫縱座標)不是最簡比,則這個點會被擋住,所以我們可以寫一個判斷函數(judge(int x,int y))來判斷一個點會不會被擋住,所以我們可以把所有點判斷一遍,這樣就產生了第一份代碼:
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,ans;
bool judge(int x,int y)//我跟你們說的判斷函數
{
if(__gcd(x,y)==1) return 0;
//__gcd()作用同gcd(),爲程序自帶,有興趣的話可以看我的另一篇文章,專門論述它的用法和優點。
else return 1;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(judge(i,j)==0) ans++;
}
printf("%d",ans);
}
但是事實證明這樣會嚴重超時,40000*40000的數據不是吹的啊。
幸運的是我們可以發現一個規律,因爲它是從左下角開始觀察的,易證明這個圖是關於從左下到右上的對角線成軸對稱的,這樣我們就可以只求一半的點數,然後*2,再減去算了兩次的對稱軸(對稱軸上有1個點),第二份代碼產生了!時間複雜度從O(N^2)降低到了O(N!):
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,ans;
bool judge(int x,int y){
if(__gcd(x,y)==1) return 0;
else return 1;
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=i;j<n;j++)//這裏是關鍵,我們只需要判斷一半的點就可以了,至於爲什麼從j=i開始,大家可以自己畫個圖驗證一下。
if(judge(i,j)==0) ans++;
ans=ans*2-1;//對稱軸算了兩遍,要減去。
printf("%d",ans);
}
更不幸的是這次不僅tle沒有改善,過小數據還因爲沒有特判wa了……
刺……刺激!
省選題真不是輕輕鬆鬆可以水過的啊……
靜靜心仔細觀察一下,發現從第三行開始符合一個神奇的魔性玩意,叫歐拉函數……怎麼早沒有發現呢?
歐拉函數:
嗯具體證明還是看看度娘吧:
闊怕
但這樣應該出正解了哇???
emmm我寫的那個代碼沒有保存qwq,以下是洛谷某大lao的題解,跟我思路很像,借鑑一下。
#include<iostream>
#include<algorithm>
#include<cstdio>
int n;
int phi(int n)
{
int ans,i,k;
if(n==1)
ans=1;
else
{
ans=n;
k=1;
for(i=2;n!=1;i+=k)
{
if(n%i==0)
{
ans/=i;
ans*=(i-1);
while(n%i==0) n/=i;
i=k;
}
}
}
return ans;
}
int ans;
int main()
{
scanf("%d",&n);
if(n==1)
{
putchar('0');
return 0;
}
for(int i=3;i<=n;++i)
ans+=phi(i-1);
printf("%d",ans*2+3);
return 0;
}
啊啊啊刺激啊,終於a了。
總結一下,這道題還是靠觀察的,核心是歐拉函數和軸對稱。
闊怕闊怕。
求贊…………