TopCoder 12910 NTT

题意

传送门 TopCoder 12910 SumOfArrays

题解

将数组表示 a , b , c a,b,c a,b,c,令 A , B , C A,B,C A,B,C 表示数组不同值出现的次数。暴力求解 O ( n 2 ) O(n^2) O(n2)

C k = ∑ i min ⁡ ( A i , B k − i ) = ∑ i ∑ j = 1 ∞ [ A i ≥ j ] [ B k − i ≥ j ] C_k = \sum\limits_i \min (A_i, B_{k-i}) = \sum_i \sum\limits_{j=1}^{\infty}[A_i\geq j][B_{k-i}\geq j] Ck=imin(Ai,Bki)=ij=1[Aij][Bkij] 后一个式子是卷积的形式。将 n n n 个小球随机放入 n n n 个盒子,那么单个盒子中最多的小球数期望为 m = O ( log ⁡ n / log ⁡ log ⁡ n ) m = O(\log n/ \log\log n) m=O(logn/loglogn),即 C C C 中出现次数最多的数字,其出现次数期望为 m m m。将上式拆开 C k = ∑ j = 1 m ∑ i [ A i ≥ j ] [ B k − i ≥ j ] + ∑ j = m + 1 ∞ ∑ i [ A i ≥ j ] [ B k − i ≥ j ] C_k = \sum\limits_{j=1}^{m}\sum_i [A_i\geq j][B_{k-i}\geq j] + \sum\limits_{j=m+1}^{\infty}\sum_i [A_i\geq j][B_{k-i}\geq j] Ck=j=1mi[Aij][Bkij]+j=m+1i[Aij][Bkij] 后一个暴力算即可。总时间复杂度 O ( n log ⁡ 2 n / log ⁡ log ⁡ n ) O(n\log^2 n/ \log\log n) O(nlog2n/loglogn)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr ll MOD = 998244353, PRT = 3;
ll qpow(ll x, ll n)
{
    ll res = 1;
    while (n > 0)
    {
        if (n & 1)
            res = res * x % MOD;
        x = x * x % MOD, n >>= 1;
    }
    return res;
}
vector<int> rev;
struct Poly : vector<ll>
{
    Poly() {}
    Poly(int n) : vector<ll>(n) {}
    Poly(const initializer_list<ll> &list) : vector<ll>(list) {}
    void fft(int n, bool inverse)
    {
        if ((int)rev.size() != n)
        {
            rev.resize(n);
            for (int i = 0; i < n; ++i)
                rev[i] = rev[i >> 1] >> 1 | (i & 1 ? n >> 1 : 0);
        }
        resize(n);
        for (int i = 0; i < n; ++i)
            if (i < rev[i])
                std::swap(at(i), at(rev[i]));

        for (int m = 1; m < n; m <<= 1)
        {
            int m2 = m << 1;
            ll _w = qpow(inverse ? qpow(PRT, MOD - 2) : PRT, (MOD - 1) / m2);
            for (int i = 0; i < n; i += m2)
                for (int w = 1, j = 0; j < m; ++j, w = w * _w % MOD)
                {
                    ll &x = at(i + j), &y = at(i + j + m), t = w * y % MOD;
                    y = x - t;
                    if (y < 0)
                        y += MOD;
                    x += t;
                    if (x >= MOD)
                        x -= MOD;
                }
        }
    }
    void dft(int n) { fft(n, 0); };
    void idft(int n)
    {
        fft(n, 1);
        for (int i = 0, inv = qpow(n, MOD - 2); i < n; ++i)
            at(i) = at(i) * inv % MOD;
    }
    Poly operator*(const Poly &p) const
    {
        auto a = *this, b = p;
        int k = 1, n = a.size() + b.size() - 1;
        while (k < n)
            k <<= 1;
        a.dft(k), b.dft(k);
        for (int i = 0; i < k; ++i)
            a[i] = a[i] * b[i] % MOD;
        a.idft(k);
        a.resize(n);
        return a;
    }
};

class SumOfArrays
{
public:
    void get(vector<int> &a, vector<int> &A)
    {
        a[0] = A[0], a[1] = A[1];
        for (int i = 2; i < a.size(); ++i)
            a[i] = ((ll)a[i - 1] * A[2] + (ll)a[i - 2] * A[3] + A[4]) % A[5];
    }
    string findbestpair(int n, vector<int> A, vector<int> &B)
    {
        vector<int> a(n), b(n);
        get(a, A), get(b, B);
        vector<int> acnt(A[5]), bcnt(B[5]);
        for (int i = 0; i < n; ++i)
            ++acnt[a[i]], ++bcnt[b[i]];
        int m = 2 * ceil(log(n) / log(log(n)));
        vector<int> cnt(A[5] + B[5]);

        for (int num = 1; num <= m; ++num)
        {
            Poly f(A[5]), g(B[5]);
            for (int i = 0; i < A[5]; ++i)
                f[i] = acnt[i] >= num;
            for (int i = 0; i < B[5]; ++i)
                g[i] = bcnt[i] >= num;
            f = f * g;
            for (int i = 0; i < f.size(); ++i)
                cnt[i] += f[i];
        }

        vector<int> ta, tb;
        for (int i = 0; i < A[5]; ++i)
            if (acnt[i] > m)
                ta.push_back(i);
        for (int i = 0; i < B[5]; ++i)
            if (bcnt[i] > m)
                tb.push_back(i);
        for (int x : ta)
            for (int y : tb)
                cnt[x + y] += min(acnt[x], bcnt[y]) - m;
                
        int _num = -1, _cnt = -1;
        for (int i = 0; i < cnt.size(); ++i)
            if (cnt[i] >= _cnt)
                _cnt = cnt[i], _num = i;
        return to_string(_cnt) + " " + to_string(_num);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值