小郑的疑惑
[link](CSUSTOJ | 小郑的疑惑)
题意
给n个数a和一个长度为m的数组res,a中两两相乘为x,[1,m]中能被x整除的位置的 r e s [ i ] res[i] res[i]加一,最后输出res数组。
题解
如果按照他给的过程模拟的话,是一个 O ( n 2 l o g n ) O(n^2logn) O(n2logn)的复杂度,一定会T。
考虑优化,我们能否将每一个 a i × a j a_i\times a_j ai×aj对应的数先预处出来,因为数据范围只有 1 e 6 1e6 1e6,因此我们可以开一个数组来存每个位置对应的数出现的次数。
发现如果 a i × a j > m a_i \times a_j > m ai×aj>m则不会对答案产生影响,因此我们只需要看 [ 1 , m ] [1,m] [1,m]这个区间内有的数,然后把他们的点对求出来即可。
就是如果本身有着数x,那么我们往后找x的倍数p,如果p的另一个因数d=p/x存在,那么能够得到这个点对的值的方法就是x的数量乘d的数量(注意x和d相等的时候应该是 C 因 子 数 量 2 C_{因子数量}^{2} C因子数量2),因为 a i × a j 和 a j × a i a_i\times a_j和a_j\times a_i ai×aj和aj×ai的贡献是一个所以我们人为的定一个序,我们只找d >= x的时候的情况,因为如果存在d < x 的情况,那么在枚举到d的时候这个p一定被算过了,这样就可以不重不漏的算出来了。然后再从前往后把每个数给他的倍数的贡献加上叠加上即可,对于每一个数给他的倍数加上,调和级数是 O ( l o g n ) O(logn) O(logn)的,然后对于每次枚举是 O ( n ) O(n) O(n)的,最后时间复杂度大概在 O ( n l o g n ) O(nlogn) O(nlogn)左右。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#include <deque>
#include <sstream>
#define x first
#define y second
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e6 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
LL st[N], res[N], cnt[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) {
int x; cin >> x;
st[x] ++;
}
for (int i = 1; i <= m; i ++ ) {
if (st[i]) {
for (int j = i; j <= m; j += i) {
LL p = j / i;
if (p == i) cnt[j] += (st[i] * (st[i] - 1)) / 2;
else if (p > i) cnt[j] += st[i] * st[p];
}
}
}
for (int i = 1; i <= m; i ++ )
for (int j = i; j <= m; j += i )
res[j] += cnt[i];
for (int i = 1; i <= m; i ++ ) cout << res[i] << ' ';
cout << endl;
return 0;
}