洛谷 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了。
总结一下,这道题还是靠观察的,核心是欧拉函数和轴对称。
阔怕阔怕。
求赞…………

猜你喜欢

转载自blog.csdn.net/floatiy/article/details/79307646