洛谷P2158 [SDOI2008]仪仗队 (线性筛欧拉函数求前缀和)

题目链接

题解:

由题意可知,从左下角的点到其他所有点连成的不同斜率的边数即为所求。要求得边长 y / x 不同的数量( y < x),则求与 x 互质的数的个数 *2+3 即可(因为只求出了一个三角形的答案数量,需要*2来求正方形矩阵的答案数量,+3是有三个特殊的边,即两条 y / x == 0 以及一条 y / x == 1)。由于边长为 n 的边的答案包含了边长为 n - 1 的边的答案,所以求个前缀和即可。最后由于他给的是点数,所以求出的前缀和下标必须减一(点数比边长多一)。

代码:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <bitset>
using namespace std;

typedef long long ll;
#define int ll
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXM 200000 + 10
#define MAXN 40000 + 10
const ll mod = 1e9 + 7;
#define P pair<int, int>
#define fir first
#define sec second

int n;
int phi[MAXN];
int prime[MAXN], judge[MAXN], cnt;

void euler()
{
    phi[1] = 0;
    for(int i = 2; i < MAXN; i ++) {
        if(!judge[i]) {
            prime[++cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && prime[j] * i < MAXN; j ++) {
            judge[i*prime[j]] = 1;
            if(i % prime[j] == 0) {
                phi[i*prime[j]] = phi[i] * prime[j];
                break;
            }
            else phi[i*prime[j]] = phi[i] * phi[prime[j]];
        }
    }
    phi[1] = 0;
    for(int i = 2; i < MAXN; i ++) {
        phi[i] += phi[i-1];
    }
}

signed main()
{
    euler();
    cin >> n;
    if(n == 1) cout << 0 << endl;
    else cout << (phi[n-1] * 2 + 3)<< endl;
}

/*

The WAM is F**KING interesting .

*/

猜你喜欢

转载自blog.csdn.net/ooB0Boo/article/details/89232374