洛谷题单指南-进阶数论-P2158 [SDOI2008] 仪仗队

原题链接:https://www.luogu.com.cn/problem/P2158

题意解读:n*n个点组成的方阵中,最左下角的点能看到多少点。

解题思路:

设左下角点的坐标是(0, 0),设从0点能看到的点是(x, y),对于看不到的点必然可以通过将(x, y)同时扩大一定倍数得到(xd, yd)

这样一来,看不到的点必然其x、y坐标的GCD不为1,能看到的点的坐标GCD为1,也就是x、y互质的点是能被看到的。

要统计方阵中一共有多少个互质的点,可以分两块来看,上三角、下三角,由于对称性,只用统计一边即可,再乘2加1,加1是因为对角线能看到1个。

对于上三角,一共有多少个点的坐标互质呢?

问题可以转化成计算从上到下每一个行互质的点数:

第1行:纵坐标n-1,横坐标1~n-1,结果就是phi(n-1)

第2行:纵坐标n-2,横坐标1~n-2,结果就是phi(n-2)

。。。

第n-1行:纵坐标1,横坐标1,结果就是phi(1)

因此,只需要用线性筛将1~n-1的phi值都预处理出来,然后加在一起即可。最后*2+1。

注意如果方阵是1*1,则一个都看不到。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 40005;
int primes[N], cnt, phi[N];
bool vis[N];
int n;

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) phi[i] = i;
    for(int i = 2; i <= n; i++)
    {   
        if(!vis[i])
        {
            primes[++cnt] = i;
            vis[i] = true;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt; j++)
        {
            if(i * primes[j] > n) break;
            vis[i * primes[j]] = true;
            if(i % primes[j] == 0)
            {
                phi[i * primes[j]] = phi[i] * primes[j];
                break;
            }
            else
            {
                phi[i * primes[j]] = phi[i] * (primes[j] - 1);
            }
        }
    }

    int ans = 0;
    if(n > 1)
    {
        for(int i = 1; i < n; i++) ans += phi[i];
        ans = ans * 2 + 1;
    }   
    cout << ans;
    return 0;
}

 

posted @ 2025-10-30 14:51  hackerchef  阅读(7)  评论(0)    收藏  举报