洛谷 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\n", sum);
//for (int i = 1; i <= sum; i++) {
// printf("x : %d y : %d\n", 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\n", 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;
}