洛谷 2158 数论 打表 欧拉函数

#洛谷 2158 数论 递推 欧拉函数 打表找规律

传送门 (https://www.luogu.org/problem/show?pid=2158#sub)


其实看到SDOI就有一种不太好的预感,,想当年那个猪国杀,,,呵呵,,

20分暴力

用二维数组维护每个点能否被选择,之后二维枚举每个点,如果没有被选择就选择,并且用它去遮挡其他所有点

#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn = 1000 + 10;

int a[maxn][maxn];
int n;
int sum;
// int ansx[maxn];
// int ansy[maxn];

void update (int x, int y) {
    int curx = x, cury = y;
    while (curx <= n && cury <= n) {
        curx += (x - 1);
        cury += (y - 1);
        a[curx][cury] = 1;
    }
}

int main () {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        a[1][i] = 1;
        a[i][1] = 1;
    }
    sum = 2;
    // ansx[1] = 1;
    // ansy[1] = 2;
    // ansx[2] = 2;
    // ansy[2] = 1;
    for (int i = 2; i <= n; i++) {
        for (int j = 2; j <= n; j++) {
            if (a[i][j] == 0) {
                a[i][j] = 1;
                sum++;
                // ansx[sum] = i;
                // ansy[sum] = j;
                update(i, j);
            }
        }
    } 
    printf("%d
", sum);
    //for (int i = 1; i <= sum; i++) {
    //     printf("x : %d  y : %d
", ansx[i], ansy[i]);
    // }

    return 0;
}

正解:

其实这道题的正解是打表打出来的,大概把数字取到10左右就能发现某种性质,

x : 1  y : 2
x : 2  y : 1
x : 2  y : 2
x : 2  y : 3
x : 2  y : 4
x : 2  y : 5
x : 2  y : 6
x : 2  y : 7
x : 2  y : 8
x : 2  y : 9
x : 2  y : 10
x : 3  y : 2
x : 3  y : 4
x : 3  y : 6
x : 3  y : 8
x : 3  y : 10
x : 4  y : 2
x : 4  y : 3
x : 4  y : 5
x : 4  y : 6
x : 4  y : 8
x : 4  y : 9
x : 5  y : 2
x : 5  y : 4
x : 5  y : 6
x : 5  y : 8
x : 5  y : 10
x : 6  y : 2
x : 6  y : 3
x : 6  y : 4
x : 6  y : 5
x : 6  y : 7
x : 6  y : 8
x : 6  y : 9
x : 6  y : 10
x : 7  y : 2
x : 7  y : 6
x : 7  y : 8
x : 8  y : 2
x : 8  y : 3
x : 8  y : 4
x : 8  y : 5
x : 8  y : 6
x : 8  y : 7
x : 8  y : 9
x : 8  y : 10
x : 9  y : 2
x : 9  y : 4
x : 9  y : 6
x : 9  y : 8
x : 9  y : 10
x : 10  y : 2
x : 10  y : 3
x : 10  y : 5
x : 10  y : 6
x : 10  y : 8
x : 10  y : 9

由此可见,每个点可取的值与phi[i-1]有关,故使用筛法线性求欧拉函数,之后根据对称性并考虑特殊点,将ans = ans * 2 + 1即为结果

#include <cstdio>
#include <algorithm>
#include <cstring>

const int maxn = 40000 + 100;

int phi[maxn], isprime[maxn], prime[maxn];
int n;
int tot = 0;
long long ans = 0;

int main () {
    scanf("%d", &n);
    phi[1] = 1;
    for (int i = 1; i <= n; i++) isprime[i] = 1;
    for (int i = 2; i <= n; i++) {
        if (isprime[i]) {
            tot++;
            prime[tot] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= tot; j++) {
            if (i * prime[j] > n) break;
            isprime[i * prime[j]] = 0;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            } else {
                phi[i * prime[j]] = phi[i] * (prime[j] - 1);
            }
        }
    }
    // for (int i = 1; i <= n; i++) printf("phi[%d] = %d
", i, phi[i]);
    for (int i = 2; i <= n; i++) {
        ans = ans + (long long)phi[i-1];
    }
    ans = ans * 2 + 1;
    printf("%lld", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/CtsNevermore/p/6023001.html