求数组有多少个数,恰好等于集合中另外两个(不同的)数之和?
注意到数集比较小,而且涉及到下标的加法,可以很自然地想到卷积
注意减去自己加自己的贡献
真是一道NTT练手好题
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 #define Dollar1 998244353 6 #define MAXN 70001 7 #define int long long 8 int lim, dig, rev[MAXN], n, sums[MAXN], A[MAXN]; 9 const int G = 3, Ginv = (Dollar1 + 1) / 3; 10 void init(int sz) { 11 lim = 1; dig = 0; 12 while (lim <= sz << 1) lim <<= 1, ++dig; 13 for (int i = 0; i != lim; ++i) 14 rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (dig - 1)); 15 } 16 inline int fastpow(int a, int b) { 17 int res = 1; 18 while (b) { 19 if (b & 1) res = res * a % Dollar1; 20 a = a * a % Dollar1; 21 b >>= 1; 22 } 23 return res; 24 } 25 void NTT(int * A, int type) { 26 for (int i = 0; i != lim; ++i) 27 if (i < rev[i]) 28 swap(A[i], A[rev[i]]); 29 for (int mid = 1; mid < lim; mid <<= 1) { 30 int Wn = fastpow(type == 1 ? G : Ginv, (Dollar1 + 1) / mid / 2); 31 for (int k = 0; k < lim; k += (mid << 1)) { 32 int W = 1; 33 for (int l = 0; l < mid; ++l, W = W * Wn % Dollar1) { 34 int X = A[l + k], Y = A[l + k + mid] * W % Dollar1; 35 A[l + k] = (X + Y) % Dollar1; 36 A[l + k + mid] = (X - Y + Dollar1) % Dollar1; 37 } 38 } 39 } 40 if (type == -1) { 41 const int liminv = fastpow(lim, Dollar1 - 2); 42 for (int i = 0; i != lim; ++i) 43 A[i] = A[i] * liminv % Dollar1; 44 } 45 } 46 signed main() { 47 cin >> n; 48 init(10000); 49 for (int i = 1; i <= n; ++i) cin >> sums[i], A[sums[i]] = 1; 50 NTT(A, 1); 51 for (int i = 0; i != lim; ++i) (A[i] *= A[i]) %= Dollar1; 52 NTT(A, -1); 53 for (int i = 1; i <= n; ++i) --A[sums[i] << 1]; 54 int ans = 0; 55 for (int i = 1; i <= n; ++i) ans += (bool)A[sums[i]]; 56 cout << ans << endl; 57 return 0; 58 }
// 这段时间代码风格有变化