首页 > 其他分享 >题解 accoders::NOI 5508【漂亮大厨(cook)】

题解 accoders::NOI 5508【漂亮大厨(cook)】

时间:2023-10-05 09:33:55浏览次数:35  
标签:return NOI int 题解 rhs accoders operator modint const

题解 accoders::NOI 5508【漂亮大厨(cook)】

part 1

区间加 \(x\),区间询问有多少个数字 \(\leq y\)。\(n,m\leq 10^5,x\leq 200,y\leq 10^7\)。

考虑 P5356 [Ynoi2017] 由乃打扑克 的做法,分块,块内按照值排序。修改就整块打 tag,散块暴力重构(可以归并排序重构);询问在整块上二分,散块暴力。这样的复杂度是 \(O(n\log n\cdot\sqrt n)\),瓶颈来自整块上的二分。由于这题不是由乃打扑克,询问可以离线,所以考虑离线所有对整块的询问(散块直接暴力不管;离线询问时先减掉加法标记;重构散块时,新建这个块的副本,以后的询问挂在副本上)。离线完了以后一共有 \(O(\sqrt n+q)\) 个整块挂了总共 \(O(q\sqrt n)\) 个询问,考虑在每个整块上对询问排序后用一个指针扫边所有块,这样就能取得所有询问的答案,做到了 \(O(n\sqrt n)\)。

part 2

\(Q\) 次询问每次给定 \(n, m\) 求 \(\sum_{i\leq m}\binom n i\)。\(Q, n, m\leq 10^5\)。

可以莫队,\(m\) 那一维就直接加或减一个组合数。\(n\) 那一维就是按照 \(\binom n m = \binom{n-1} m + \binom{n-1}{m-1}\) 展开一次,变成两倍的形式再调调系数。

code

// ubsan: undefined
// accoders
#include <tuple>
#include <cstdio>

#include <vector>
#include <cstring>
#include <cassert>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template <unsigned P>
struct modint {
    unsigned v;
    modint() : v(0) {}
    template <class T>
    modint(T x) {
        x %= (int)P, v = x < 0 ? x + P : x;
    }
    modint operator+() const { return *this; }
    modint operator-() const { return modint(0) - *this; }
    modint inv() const { return assert(v), qpow(*this, P - 2); }
    friend int raw(const modint &self) { return self.v; }
    template <class T>
    friend modint qpow(modint a, T b) {
        modint r = 1;
        for (; b; b >>= 1, a *= a)
            if (b & 1)
                r *= a;
        return r;
    }
    modint &operator+=(const modint &rhs) {
        if (v += rhs.v, v >= P)
            v -= P;
        return *this;
    }
    modint &operator-=(const modint &rhs) {
        if (v -= rhs.v, v >= P)
            v += P;
        return *this;
    }
    modint &operator*=(const modint &rhs) {
        v = 1ull * v * rhs.v % P;
        return *this;
    }
    modint &operator/=(const modint &rhs) { return *this *= rhs.inv(); }
    friend modint operator+(modint lhs, const modint &rhs) { return lhs += rhs; }
    friend modint operator-(modint lhs, const modint &rhs) { return lhs -= rhs; }
    friend modint operator*(modint lhs, const modint &rhs) { return lhs *= rhs; }
    friend modint operator/(modint lhs, const modint &rhs) { return lhs /= rhs; }
    friend bool operator==(const modint &lhs, const modint &rhs) { return lhs.v == rhs.v; }
    friend bool operator!=(const modint &lhs, const modint &rhs) { return lhs.v != rhs.v; }
};
typedef modint<998244353> mint;
template <int N>
struct C_prime {
    mint fac[N + 10], ifac[N + 10];
    C_prime() {
        fac[0] = ifac[0] = 1;
        for (int i = 1; i <= N; i++) fac[i] = fac[i - 1] * i;
        ifac[N] = 1 / fac[N];
        for (int i = N; i >= 1; i--) ifac[i - 1] = ifac[i] * i;
    }
    mint operator()(int n, int m) const { return n >= m ? fac[n] * ifac[m] * ifac[n - m] : 0; }
};
template <int N, int unit>
struct Jason42 {
    vector<pair<int, int>> blo[N / unit + 10];
    int bel[N + 10], tag[N / unit + 10];
    void init(int n, int b[]) {
        for (int i = 1; i <= n; i++) bel[i] = (i - 1) / unit + 1;
        for (int i = 1; i <= n; i++) {
            blo[bel[i]].emplace_back(b[i], i);
        }
        for (int b = 1; b <= bel[n]; b++) sort(blo[b].begin(), blo[b].end());
    }
    void modify_bf(int L, int R, int b, int k) {
        for (auto &ccf : blo[b]) {
            if (L <= ccf.second && ccf.second <= R)
                ccf.first += k;
        }
        sort(blo[b].begin(), blo[b].end());
    }
    void modify(int L, int R, int k) {
        int Lb = bel[L], Rb = bel[R];
        for (int i = Lb + 1; i < Rb; i++) tag[i] += k;
        if (Lb == Rb)
            modify_bf(L, R, Lb, k);
        else
            modify_bf(L, R, Lb, k), modify_bf(L, R, Rb, k);
    }
    int count_bf(int L, int R, int b, int lim) {
        int ans = 0;
        for (auto &ccf : blo[b]) {
            if (L <= ccf.second && ccf.second <= R)
                ans += ccf.first + tag[b] <= lim;
        }
        return ans;
    }
    int count_blo(int b, int lim) {
        // int bfr = count_bf(1, N, b, lim);
        int orz = upper_bound(blo[b].begin(), blo[b].end(), pair<int, int>{ lim - tag[b], 998244353 }) -
                  blo[b].begin();
        // if (bfr != orz) debug("count: bf = %d, st = %d, assert(0)\n", bfr, orz);
        return orz;
    }
    int count(int L, int R, int lim) {
        int Lb = bel[L], Rb = bel[R], ans = 0;
        for (int i = Lb + 1; i < Rb; i++) ans += count_blo(i, lim);
        if (Lb == Rb)
            ans += count_bf(L, R, Lb, lim);
        else
            ans += count_bf(L, R, Rb, lim) + count_bf(L, R, Lb, lim);
        return ans;
    }
};
template <int N, int unit>
struct RobinBruteForce {
    int a[N + 10];
    void init(int n, int b[]) {
        for (int i = 1; i <= n; i++) a[i] = b[i];
    }
    void modify(int L, int R, int k) {
        for (int i = L; i <= R; i++) a[i] += k;
    }
    int count(int L, int R, int lim) {
        int ans = 0;
        for (int i = L; i <= R; i++) ans += a[i] <= lim;
        return ans;
    }
};
mint ans[1 << 17];
C_prime<1 << 17> binom;
struct Moqueue {
    vector<tuple<int, int, int>> vec;
    void add(int n, int m, int id) {
        debug("add(%d, %d, %d)\n", n, m, id);
        vec.emplace_back(n, min(n, m), id);
    }
    void answer() {
        sort(vec.begin(), vec.end(), [](tuple<int, int, int> a, tuple<int, int, int> b) {
            int l1, r1, l2, r2;
            tie(l1, r1, ignore) = a, tie(l2, r2, ignore) = b;
            return l1 / 356 != l2 / 356 ? l1 < l2 : r1 < r2;
        });
        int nowl = 0, nowr = 0;
        mint now = 1, inv2 = mint(1) / 2;
        for (auto &&qry : vec) {
            int l, r, id;
            tie(l, r, id) = qry;
            // l >= r
            while (nowl < l) now = now * 2 - binom(nowl++, nowr);
            while (nowr > r) now -= binom(nowl, nowr--);
            while (nowl > l) now = (now + binom(--nowl, nowr)) * inv2;
            while (nowr < r) now += binom(nowl, ++nowr);
            ans[id] = now;
        }
    }
};
int n, Q, Qcnt, ia[1 << 17];
Jason42<100010, 356> Robin;
// RobinBruteForce<100010, 356> Robin;
Moqueue moqueue;
int main() {
    freopen("cook.in", "r", stdin);
    freopen("cook.out", "w", stdout);
    scanf("%d%d", &n, &Q);
    for (int i = 1; i <= n; i++) scanf("%d", ia + i);
    Robin.init(n, ia);
    for (int i = 1, op, l, r, x, y, k; i <= Q; i++) {
        scanf("%d%d%d", &op, &l, &r);
        if (op == 1)
            scanf("%d", &x), Robin.modify(l, r, x);
        else
            scanf("%d%d", &y, &k), moqueue.add(Robin.count(l, r, y), k, ++Qcnt);
    }
    moqueue.answer();
    for (int i = 1; i <= Qcnt; i++) printf("%d\n", raw(ans[i]));
    return 0;
}

标签:return,NOI,int,题解,rhs,accoders,operator,modint,const
From: https://www.cnblogs.com/caijianhong/p/17743063.html

相关文章

  • P9019 [USACO23JAN] Tractor Paths P 题解
    Description有\(n\)个区间,第\(i\)个区间为\([l_i,r_i]\)。保证\(l_1<l_2<\cdots<l_n\)且\(r_1<r_2<\cdots<r_n\)。其中一部分区间是特殊的,输入会给定。如果第\(i\)个区间和第\(j\)个区间相交,那么\(i,j\)之间有一条边。保证\(1,n\)联通。给定\(Q\)组询问,每次......
  • 题解 P9701【[GDCPC2023] Classic Problem】
    题如其名,确实挺经典的。我们称边权在输入中给定的边为特殊边,其它边为平凡边。称特殊边涉及到的点为特殊点,其它点为平凡点。显然,对于连续的若干平凡点\([l,r]\),他们内部的最优连边方式就是连成一条链,花费\(r-l\)的代价。我们先把这样的代价加到答案中,然后将极长连续平凡点缩成......
  • [SHOI2009] 会场预约 题解
    LG任意时刻每个点最多被一条线段覆盖暴力删每条线段是对的插入\([l,r]\)时需要删除的线段要么被\([l,r]\)包含,要么覆盖\(l\)或\(r\)性质非常强所以做法非常多一种比较神奇的:对于两条线段\([l_{1},r_{1}],[l_{2},r_{2}]\),定义<为\(r_{1}<l_{2}\),即线段\(1\)完......
  • 题解 accoders::NOI 5511【漂亮轰炸(bomb)】
    题解accoders::NOI5511【漂亮轰炸(bomb)】http://47.92.197.167:5283/contest/406/problem/4BZOJ3252是弱化版。problem一棵树,边带权。\(Q\)次询问,给定\(k\)和一个首都点,选择\(k\)条路径轰炸,其中必须由一轮要轰炸首都,但没有要求每条路径都经过首都。每条边只能被炸一次,......
  • 题解 P9695【[GDCPC2023] Traveling in Cells】
    显然,询问的答案即为\(x\)所在的极长的满足颜色均在\(\mathbb{A}\)内的连续段的权值和。如果我们能维护对颜色的单点修改,以及求出某个位置所在极长连续段的左右端点\(l,r\),只需要树状数组即可求出答案。一个朴素的想法是对每种颜色开一棵线段树,单点修改是平凡的,极长连续段左......
  • 题解 P9702【[GDCPC2023] Computational Geometry】
    这题一看就不是计算几何,考虑区间DP。设凸多边形的\(n\)个顶点依次为\(P_1,P_2,\cdots,P_n\)。设\(f_{i,j}\)在\(i<j\)时表示\(P_i,P_{i+1},\cdots,P_{j-1},P_j\)组成的多边形的直径的平方,在\(i>j\)时表示\(P_i,P_{i+1},\cdots,P_n,P_1,\cdots,P_{j-1},P_j\)组......
  • 题解 P9697【[GDCPC2023] Canvas】
    好题。后面的操作会覆盖前面的操作,这东西不好处理,我们不妨时光倒流,将问题转化为一个位置一旦被填了数,就再也不会变了。如果解决了这个问题,只需将操作序列倒过来,就得到了原问题的解。显然,所有\(x_i=y_i=2\)的操作会最先被执行,所有\(x_i=y_i=1\)的操作会最后被执行。只需要给......
  • 高橋君 题解
    AtCoder天下一プログラマーコンテスト2014本戦高橋君题意给定\(n,k\),求\[\sum\limits_{i=0}^{k}\dbinom{n}{i}\]多测,\(1\len,k,T\le10^5\)。题解可以考虑使用莫队求解,下文讲述如何移动指针。\(n\rightarrown+1\)根据杨辉三角递推式\(\dbinom{n}{m}=......
  • 题解 P2674 《瞿葩的数字游戏》T2-多边形数
    题目描述给你一个正整数数\(n\),问你它是不是多边形数\(K\),如果是,设\(K_1\)是最小的\(K\),\(K_2\)是次小的\(K\),输出\(K_1\)和\(K_2\)。具体思路我们主要来看上面这张表里有什么规律。性质1:\(1\)是任何一个多边形数。性质2:\(2\)不是任何一个多边形数。性......
  • 情报破译 题解
    \(d<n\le2e5,m\le10,1\lep\le10^9,0\lea_i\le1e9\)每个位运算之间独立,所以我们可以构造一个\(\{2^{k-1},2^{k-1}.....\}\)和一个\(\{0,0,0...\}\)的数组,让他们倍增去做如上运算,最后用他们把\(p\)轮拼出来,我们就知道了\(i\)位置上二进制下第\(j\)位如果是\(0\)......