[NOIP2014普及组T1]珠心算测验 - NTT

求数组有多少个数,恰好等于集合中另外两个(不同的)数之和?

注意到数集比较小,而且涉及到下标的加法,可以很自然地想到卷积

注意减去自己加自己的贡献

真是一道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;
}
posted @ 2018-08-12 18:24  daklqw  阅读(584)  评论(1编辑  收藏  举报