洛谷 P2158 儀仗隊

//全網最詳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了。
總結一下,這道題還是靠觀察的,核心是歐拉函數和軸對稱。
闊怕闊怕。
求贊…………

發佈了35 篇原創文章 · 獲贊 52 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章