Submission #68384376


Source Code Expand

#pragma GCC optimize(3)
#pragma GCC optimize("Ofast,unroll-loops")
#include <cassert>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ostream>
#include <set>
using namespace std;

using ll = long long;
using ull = unsigned long long;

const ll N = 2e5 + 5, SQRT = 400;

template <ll MOD>
struct modular {
    ll x;
    modular() : x(0) {}
    modular(ll _x) {
        x = _x;
        x %= MOD;
        if (x < 0) {
            x += MOD;
        }
        return;
    }
    modular operator+(const modular &b) const { return modular(x + b.x); }
    modular operator*(const modular &b) const { return modular(x * b.x); }
    modular operator+=(const modular &b) { return *this = *this + b; }
    modular operator*=(const modular &b) { return *this = *this * b; }
};

using mll = modular<(ll)998244353>;

mll prod[N], inv_prod[N];
mll C(ll n, ll m) {
    if (m < 0 || m > n) {
        return 0;
    }
    return prod[n] * inv_prod[m] * inv_prod[n - m];
}
mll qp(mll a, ll b) {
    mll ret = 1;
    for (; b; b >>= 1, a *= a) {
        if (b & 1) {
            ret *= a;
        }
    }
    return ret;
}
mll inv(mll x) { return qp(x, 998244353 - 2); }

ll n;

mll ways(ll n, ll m, bool beg, bool ed) {
    // n 中取 m 个,头/尾是否要选。
    if (n == 1) {
        if (beg != ed) {
            return 0;
        }
        else {
            if ((m == 1 && beg) || (m == 0 && !beg)) {
                return 1;
            }
            else {
                return 0;
            }
        }
    }
    return C(n - m - 1, m - beg - ed);
}

struct blk_stat {
    mll cnt[2][2];
    blk_stat operator*(const blk_stat &b) const {
        blk_stat ret;
        for (ll i = 0; i < 2; i++) {
            for (ll j = 0; j < 2; j++) {
                ret.cnt[i][j] = ((cnt[i][1] + cnt[i][0]) * b.cnt[0][j] +
                                 cnt[i][0] * b.cnt[1][j]);
            }
        }
        return ret;
    }
    blk_stat(const blk_stat &b) { memcpy(cnt, b.cnt, sizeof(cnt)); }
    blk_stat(blk_stat &&) = default;
    blk_stat &operator=(const blk_stat &) = default;
    blk_stat() { memset(cnt, 0, sizeof(cnt)); }
};

blk_stat ways_cnt[N]; // 对于没有限制的情况,维护这个。

struct segment {
    ll l, r;
    ll nxt_blk;
    blk_stat st; // 第一位/最后一位是咖啡/茶的方案数。
    ll num_coffee;
    ll sum_coffee;
    ll size() { return r - l + 1; }
    void get_stat() {
        if (sum_coffee == -1) {
            st = ways_cnt[size() - 1];
            return;
        }
        // 根据 num_coffee 和 size() 求出 st
        for (ll i = 0; i < 2; i++) {
            for (ll j = 0; j < 2; j++) {
                st.cnt[i][j] = ways(size(), num_coffee, i, j);
            }
        }
    }
};

ll blk_len;
segment seg[N];
ll i2blk[N];
pair<ll, ll> rng[N / SQRT];

bool cur_val[N];
set<ll> s;

void recal(ll blk) {
    ll blk_beg = rng[blk].first, blk_end = rng[blk].second;
    bool flag = true;
    for (ll i = blk_end - 1; i >= blk_beg; i--) {
        auto &cur_seg = seg[i], &nxt_seg = seg[seg[i].r + 1];
        if (cur_val[i]) {
            if (flag) {
                cur_seg.nxt_blk = cur_seg.r + 1;
                assert(cur_seg.r + 1 >= blk_end);
                cur_seg.get_stat();
                flag = false;
            }
            else {
                cur_seg.nxt_blk = nxt_seg.nxt_blk;
                cur_seg.get_stat();
                cur_seg.st = cur_seg.st * nxt_seg.st;
            }
        }
    }
    return;
}

void cut(ll l, ll pos, ll lval) {
    auto sum_coffee_prev = seg[l].sum_coffee - seg[l].num_coffee;
    ll rval = seg[l].sum_coffee, r = seg[l].r;
    seg[l].sum_coffee = lval;
    seg[l].num_coffee = lval - sum_coffee_prev;
    seg[l].r = pos;
    seg[l].l = l;
    if (pos == r) {
        seg[r + 1].num_coffee = seg[r + 1].sum_coffee - lval;
        recal(i2blk[l]);
        recal(i2blk[(r + 1)]);
        return;
    }
    seg[pos + 1].l = pos + 1;
    seg[pos + 1].r = r;
    seg[pos + 1].sum_coffee = rval;
    seg[pos + 1].num_coffee = rval - lval;
    // 最后一段固定个数为 -1,没有限制.
    s.insert(pos + 1);
    cur_val[pos + 1] = true;
    recal(i2blk[l]);
    if (i2blk[(pos + 1)] != i2blk[l]) recal(i2blk[(pos + 1)]);
}

void merge(ll posl, ll posr) {
    auto sum_coffee_prev = seg[posl].sum_coffee - seg[posl].num_coffee;
    seg[posl].l = posl;
    seg[posl].r = seg[posr].r;
    seg[posl].sum_coffee = seg[posr].sum_coffee;
    seg[posl].num_coffee = seg[posl].sum_coffee - sum_coffee_prev;
    s.erase(posr);
    cur_val[posr] = false;
    recal(posl / blk_len);
}

void modify(ll pos, ll val) {
    auto it = s.lower_bound(pos);
    if (it == s.end() || *it > pos) {
        it--;
    }
    if (val == -1) {
        //assert(*it == pos);
        if (s.find(pos + 1) == s.end()) {
            return;
        }
        auto r = s.lower_bound(pos + 1);
        merge(*it, *r);
    }
    else {
        cut(*it, pos, val);
    }
}

mll query() {
    blk_stat cnt;
    cnt.cnt[0][0] = 1;
    ll cur = 0;
    while (cur < n) {
        cnt = cnt * seg[cur].st;
        cur = seg[cur].nxt_blk;
    }
    return cnt.cnt[0][0] + cnt.cnt[0][1];
}

ll q;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> q;
    prod[0] = 1;
    inv_prod[0] = 1;
    for (ll i = 1; i <= n; i++) {
        prod[i] = prod[i - 1] * i;
        inv_prod[i] = inv(prod[i]);
    }
    ways_cnt[0].cnt[0][0] = 1;
    ways_cnt[1].cnt[0][0] = 1;
    ways_cnt[1].cnt[1][1] = 1;
    blk_len = sqrt(n);
    i2blk[0] = 0 / blk_len;
    i2blk[1] = 1 / blk_len;
    for (ll i = 2; i <= n; i++) {
        ways_cnt[i] = ways_cnt[i - 1] * ways_cnt[1];
        i2blk[i] = i / blk_len;
    }
    seg[0].l = 0;
    seg[0].r = n;
    seg[0].num_coffee = -1;
    seg[0].sum_coffee = -1;
    cur_val[0] = true;
    s.insert(0);
    for (ll i = 0; i <= (n - 1) / blk_len; i++) {
        rng[i].first = i * blk_len;
        rng[i].second = min((i + 1) * blk_len, n);
        recal(i);
    }
    while (q--) {
        ll x, y;
        cin >> x >> y;
        x--;
        modify(x, y);
        cout << query().x << "\n";
    }
    cout << flush;
    return 0;
}

/*
n 选 i 个不相邻的数就是 C(n-i+1,i)
就相当于是这几段拼起来。
有几种操作,切开一个序列,合并两个序列。
每次还要计算答案。呃呃。
拼起来的时候还需要注意。
如果上一个的最后一个是咖啡,那么第一个就只能是茶。
否则茶和咖啡都可。将两种方案记作 c_i,t_i
c_i = c_{i-1} * C(len-k-1,i-1) + t_{i-1} * C(len-k+1-1,i-1)
t_i = c_{i-1} * C(len-k-1,i) + t_{i-1} * C(len-k+1-1,i)
哦,是不是可以从后往前和从前往后扫一遍。然后再填出中间的情况。
那你就需要连着后面一起更新。
考虑线段树分治。首先切和合并显然是满足交换律的。
但是这道题难点不在于切和合并,在于如何高校的切。
考虑分块。
感觉可做。
*/

Submission Info

Submission Time
Task F - We're teapots
User Sving1024
Language C++ 20 (gcc 12.2)
Score 550
Code Size 7252 Byte
Status AC
Exec Time 1972 ms
Memory 32300 KiB

Judge Result

Set Name Sample All
Score / Max Score 0 / 0 550 / 550
Status
AC × 1
AC × 17
Set Name Test Cases
Sample 00-sample-01.txt
All 00-sample-01.txt, 01-01.txt, 01-02.txt, 01-03.txt, 01-04.txt, 01-05.txt, 01-06.txt, 01-07.txt, 01-08.txt, 01-09.txt, 01-10.txt, 01-11.txt, 01-12.txt, 01-13.txt, 01-14.txt, 01-15.txt, 01-16.txt
Case Name Status Exec Time Memory
00-sample-01.txt AC 9 ms 26928 KiB
01-01.txt AC 15 ms 27084 KiB
01-02.txt AC 17 ms 26900 KiB
01-03.txt AC 12 ms 26968 KiB
01-04.txt AC 216 ms 28344 KiB
01-05.txt AC 233 ms 28496 KiB
01-06.txt AC 241 ms 28688 KiB
01-07.txt AC 240 ms 28720 KiB
01-08.txt AC 1724 ms 30844 KiB
01-09.txt AC 1910 ms 32300 KiB
01-10.txt AC 1910 ms 32168 KiB
01-11.txt AC 1972 ms 32128 KiB
01-12.txt AC 1588 ms 32224 KiB
01-13.txt AC 645 ms 28436 KiB
01-14.txt AC 341 ms 28288 KiB
01-15.txt AC 540 ms 28732 KiB
01-16.txt AC 614 ms 28732 KiB