求数组有多少个数,恰好等于集合中另外两个(不同的)数之和?
注意到数集比较小,而且涉及到下标的加法,可以很自然地想到卷积
注意减去自己加自己的贡献
真是一道NTT练手好题
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define Dollar1 998244353
#define MAXN 70001
#define int long long
int lim, dig, rev[MAXN], n, sums[MAXN], A[MAXN];
const int G = 3, Ginv = (Dollar1 + 1) / 3;
void init(int sz) {
lim = 1; dig = 0;
while (lim <= sz << 1) lim <<= 1, ++dig;
for (int i = 0; i != lim; ++i)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (dig - 1));
}
inline int fastpow(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % Dollar1;
a = a * a % Dollar1;
b >>= 1;
}
return res;
}
void NTT(int * A, int type) {
for (int i = 0; i != lim; ++i)
if (i < rev[i])
swap(A[i], A[rev[i]]);
for (int mid = 1; mid < lim; mid <<= 1) {
int Wn = fastpow(type == 1 ? G : Ginv, (Dollar1 + 1) / mid / 2);
for (int k = 0; k < lim; k += (mid << 1)) {
int W = 1;
for (int l = 0; l < mid; ++l, W = W * Wn % Dollar1) {
int X = A[l + k], Y = A[l + k + mid] * W % Dollar1;
A[l + k] = (X + Y) % Dollar1;
A[l + k + mid] = (X - Y + Dollar1) % Dollar1;
}
}
}
if (type == -1) {
const int liminv = fastpow(lim, Dollar1 - 2);
for (int i = 0; i != lim; ++i)
A[i] = A[i] * liminv % Dollar1;
}
}
signed main() {
cin >> n;
init(10000);
for (int i = 1; i <= n; ++i) cin >> sums[i], A[sums[i]] = 1;
NTT(A, 1);
for (int i = 0; i != lim; ++i) (A[i] *= A[i]) %= Dollar1;
NTT(A, -1);
for (int i = 1; i <= n; ++i) --A[sums[i] << 1];
int ans = 0;
for (int i = 1; i <= n; ++i) ans += (bool)A[sums[i]];
cout << ans << endl;
return 0;
}