ビビジランテソンテネグロホメストーニカルマンドーレポポス

さあ、いつまでたってもオレンジコーダーになれない子はどんどんしまっちゃおうねー

【OUPC β】ビブンケイスウ 解説

問題

解説

クエリ2で聞かれている  g'(0) の値は、
x についての多項式 g(x) の1次の項の係数に等しい。

例えば、 g(x) が2つの1次関数をかけ合わせたものである場合、
すなわち、 g(x) = (ax+b) (cx+d) とすると、  g(x) = acx^ 2 + (ad+bc) x + bd であり、 g'(0) = ad+bcとなる。

ここで、g(x) が複数の1次関数をかけ合わせたものであることから、
2次以上の項を無視して良いことがわかる。
すなわち、1次関数をかけ合わせるたびに2次以上の項を捨てることで、
より効率的な計算が可能となる。

以上より、1次関数  f(x) = ax+b (a, b) と表記し、
2つの1次関数に対する演算  * (a, b) * (c, d) = (ad+bc, bd) と定義したとき、 クエリ2において、 f_l(x) * \cdots * f_r(x) = (g'(0), g(0)) となることがわかる。

後は、1次関数と演算 *をSegment Treeに載せれば解ける。

Writer解

#include <iostream>
#include <vector>

using namespace std;

using ll = long long;
using P = pair<ll, ll>;

const ll MOD = 1000000007;

template <class Monoid>
struct SegmentTree {
    using T = typename Monoid::T;

    int n;
    vector<T> data;

    SegmentTree() {}

    SegmentTree(int size, T initial_value = Monoid::unit()) {
        n = 1;
        while (n < size) n <<= 1;
        data.assign(2 * n - 1, initial_value);

        if (initial_value != Monoid::unit()) {
            for (int i = n - 2; i >= 0; i--) data[i] = Monoid::merge(data[i * 2 + 1], data[i * 2 + 2]);
        }
    }

    SegmentTree(const vector<T>& v) {
        int size = v.size();
        n = 1;
        while (n < size) n <<= 1;
        data.assign(2 * n - 1, Monoid::unit());

        for (int i = 0; i < size; i++) data[i + n - 1] = v[i];
        for (int i = n - 2; i >= 0; i--) data[i] = Monoid::merge(data[i * 2 + 1], data[i * 2 + 2]);
    }

    void update(int k, T x) {
        k += n - 1; //葉の節点
        Monoid::update(data[k], x);
        while (k > 0) {
            k = (k - 1) / 2;
            data[k] = Monoid::merge(data[k * 2 + 1], data[k * 2 + 2]);
        }
    }

    //区間[a, b)に対するクエリに答える
    //k:節点番号, [l, r):節点に対応する区間
    T query(int a, int b, int k, int l, int r) {
        //[a, b)と[l, r)が交差しない場合
        if (r <= a || b <= l) return Monoid::unit();
        //[a, b)が[l, r)を含む場合、節点の値
        if (a <= l && r <= b) return data[k];
        else {
            //二つの子をマージ
            T vl = query(a, b, k * 2 + 1, l, (l + r) / 2);
            T vr = query(a, b, k * 2 + 2, (l + r) / 2, r);
            return Monoid::merge(vl, vr);
        }
    }

    //外から呼ぶ用
    T query(int a, int b) {
        return query(a, b, 0, 0, n);
    }
};

// (1次の係数, 0次の係数)をpair<ll, ll>で持つ
template <class U = P>
struct RangeQuery {
    using T = U;
    static T merge(const T& x, const T& y) { 
        return T((x.first * y.second % MOD + x.second * y.first % MOD) % MOD, x.second * y.second % MOD);
    }
    static void update(T& target, const T& x) { target = x; }
    static constexpr T unit() { return T(0, 1); }
};

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    
    int n;
    cin >> n;
    vector<ll> a(n), b(n);
    vector<P> v;
    for (int i = 0; i < n; ++i) {
        cin >> a[i] >> b[i];
        v.emplace_back(a[i], b[i]);
    }

    SegmentTree<RangeQuery<> > st(v);

    int q;
    cin >> q;
    for (int loop = 0; loop < q; ++loop) {
        int query_no;
        cin >> query_no;

        if (query_no == 1) {
            int p;
            ll c, d;
            cin >> p >> c >> d;
            --p;
            st.update(p, P(c, d));
        } else if (query_no == 2) {
            int l, r;
            cin >> l >> r;
            --l;
            
            cout << st.query(l, r).first << "\n";
        }
    }
    return 0;
}