半在线卷积的实现

· · 个人记录

为了简明,只写二叉。

递归的方式已经被大家熟知,即 CDQ 分治。

struct PolyCDQ {
    int deg, lim;
    Poly ret, F, nf[32];
    std::function<void(int)> relax;

    PolyCDQ(Poly tf) : F(tf) {
        deg = F.size(), lim = get_lim(deg);
        ret.resize(lim);
    }

    void cdq(int l, int r) {
        int len = r - l, mid = (l + r) / 2;
        if (r - l <= 64) {
            for (int i = l; i < std::min(r, deg); ++i) {
                for (int j = l; j < i; ++j)
                    ret[i] += ret[j] * F[i - j];
                relax(i);
            }
            return;
        }
        cdq(l, mid);
        if (mid > deg)
            return;
        Poly a = Poly(ret.begin() + l, len / 2);
        Poly &b = nf[std::__lg(len)];
        if (b.empty()) {
            b = F.cut(len);
        }
        mul(a, b, len);
        for (int i = len / 2; i < len; i++)
            ret[l + i] += a[i];
        cdq(mid, r);
    }

    Poly exp() {
        pre_inv(lim);
        for (int i = 0; i < deg; i++)
            F[i] *= i;
        relax = [&](int i) {
            ret[i] = i == 0 ? 1 : ret[i] * Inv[i];
        };
        cdq(0, lim);
        return ret.cut(deg);
    }

    Poly inv() {
        m32 iv = F[0].inv();
        relax = [&](int i) {
            ret[i] = i == 0 ? iv : -ret[i] * iv;
        };
        cdq(0, lim);
        return ret.cut(deg);
    }

    Poly quo(Poly h) { // 注意是 h / f
        h.resize(lim);
        m32 iv = F[0].inv();
        relax = [&](int i) {
            ret[i] = i == 0 ? h[0] * iv : (h[i] - ret[i]) * iv;
        };
        cdq(0, lim);
        return ret.cut(deg);
    }
};

我想了想,好像迭代树和树状数组挺像的。被 hly1204 的 next 方式启发,于是有了以下的非递归写法。

struct PolyCDQ {
    int deg, lim, now = 1;
    const int M = 64;
    Poly ret, F, nf[32];

    PolyCDQ(Poly tf) : F(tf) {
        deg = F.size(), lim = get_lim(deg);
        ret.resize(lim);
    }

    m32 next() {
        int len = now & -now, l = now - len;
        if (len < M) {
            for (int j = now & -M; j < now; ++j)
                ret[now] += ret[j] * F[now - j];
        } else {
            Poly a = ret.cut(len, l), &b = nf[__lg(len)];
            if (b.empty())
                b = F.cut(len * 2);
            mul(a, b, len * 2);
            for (int i = len; i < len * 2; i++)
                ret[l + i] += a[i];
        }
        return ret[now++];
    }

    Poly inv() {
        m32 iv = F[0].inv();
        ret[0] = iv;
        for (int i = 1; i < deg; i++)
            ret[i] = -next() * iv;
        return ret.cut(deg);
    }

    Poly exp() {
        pre_inv(lim);
        for (int i = 0; i < deg; i++)
            F[i] *= i;
        ret[0] = 1;
        for (int i = 1; i < deg; i++)
            ret[i] = next() * Inv[i];
        return ret.cut(deg);
    }

    Poly quo(Poly h) { // 注意是 h / f
        h.resize(deg);
        m32 iv = F[0].inv();
        ret[0] = h[0] * iv;
        for (int i = 1; i < deg; i++)
            ret[i] = (h[i] - next()) * iv;
        return ret.cut(deg);
    }
};

在线的还在搞。

感觉 sqrt 可以半在线的实现,但是我不会。 弄出来了一个,但是跑的好慢。

struct PolyCDQS {
    int deg, lim, now = 1;
    static const int M = 64;
    Poly ret, &F = ret, nf[32], h;

    PolyCDQS(Poly tf) : h(tf) {
        deg = h.size(), lim = get_lim(deg);
        ret.resize(lim);
    }

    m32 next() {
        int len = now & -now, l = now - len;
        if (len < M) {
            if (now >= M) {
                for (int j = now & -M; j < now; ++j)
                    ret[now] += ret[j] * F[now - j] * 2;
            } else {
                for (int j = now & -M; j < now; ++j)
                    ret[now] += ret[j] * F[now - j];
            }
        } else {
            Poly a = ret.cut(len, l), &b = nf[__lg(len)];
            if (b.empty())
                b = F.cut(len * 2);
            if (l == len * 2)
                b = F.cut(len * 2) * Poly{2};
            mul(a, b, len * 2);
            for (int i = len; i < len * 2; i++) {
                ret[l + i] += a[i];
            }
        }
        return ret[now++];
    }

    Poly sqrt() {
        fill(F.begin(), F.end(), 0);
        F[0] = ret[0] = h[0].sqrt();
        m32 iv = (F[0] * 2).inv();
        for (int i = 1; i < deg; i++) {
            F[i] = ret[i] = (h[i] - next()) * iv;
        }
        return ret.cut(deg);
    }
};

在线的也造出来了。见 #150. 挑战多项式