P3396 哈希冲突 TJ
思路
暴力 \(O(n^2).\) 期望得分: \(9.\)
考虑到此题有两种做法:
\(1.\) \(O(n^2)\) 复杂度,每次询问的时候累加:
ans = 0;
for (int q = y;q <= n;q += x) {
ans += a[q];
}
printf ("%d\n",ans);
\(2.\) \(O(n^2)\) 的时间预处理,设 \(f[i][j]\) 表示模 \(i\) 等于 \(j\) 的数字总和。
for (int q = 1;q <= n;++ q) {
for (int w = 1;w <= n;++ w) {
f[w][q % w] += a[q];
}
}
于是便有了一种新的思想,根号分治。
顾名思义,根号分治就是前 \(\sqrt{n}\) 的数据预处理,剩下的暴力求解。
预处理复杂度为 \(O(n\sqrt{n})\) ,那么剩下的询问 \(p\) 就超过了 \(\sqrt{n}\) ,此时只有不到 \(\sqrt{n}\) 个数对他有贡献,每一次暴力的复杂度 \(O(\sqrt{n})\)
剩下还有更改,需要 \(O(\sqrt{n})\) 的时间处理 \(f\) 数组。
总的时间复杂度是 \(O((m + n)\sqrt{n}).\)
代码
暴力:(小学生都会写的)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 1e5 + 50010;
int n, m ,a[MAXN] ,ans;
int main () {
scanf ("%d%d",&n ,&m);
for (int q = 1;q <= n;++ q)
scanf ("%d",&a[q]);
int x ,y;
while (m --) {
ans = 0;
char ch = getchar ();
while (ch != 'A' && ch != 'C') ch = getchar ();
scanf ("%d%d",&x ,&y);
if (ch == 'A') {
for (int q = 1;q <= n;++ q) {
if (q % x == y) ans += a[q];
}
printf ("%d\n",ans);
}
else a[x] = y;
}
return 0;
}
正解:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 1e5 + 50010;
int n ,m ,a[MAXN];
int f[401][401];//f[i][j]表示模 i 余 j 的池的数的总和
int main () {
scanf ("%d%d",&n ,&m);
for (int q = 1;q <= n;++ q)
scanf ("%d",&a[q]);
int siz = sqrt (n);
for (int q = 1;q <= n;++ q) {
for (int w = 1;w <= siz;++ w) {
f[w][q % w] += a[q];
}
}
// for (int q = 1;q <= siz;++ q) {
// for (int w = 1;w <= siz;++ w) {
// printf ("mod %d = %d sum: %d\n",q ,w ,f[q][w]);
// }
// }
int x ,y ,ans;
char ch;
while (m --) {
ch = getchar ();
while (ch != 'A' && ch != 'C') ch = getchar ();
scanf ("%d%d",&x ,&y);
if (ch == 'A') {
if (x <= siz) {
printf ("%d\n",f[x][y]);
}
else {
ans = 0;
for (int q = y;q <= n;q += x) {
ans += a[q];
}
printf ("%d\n",ans);
}
}
else {
int add_ = y - a[x];
for (int w = 1;w <= siz;++ w) {
f[w][x % w] += add_;
}
a[x] = y;
}
}
return 0;
}
cb

浙公网安备 33010602011771号