【题解】P7416 [USACO21FEB] No Time to Dry P

这一类问题考虑离线下来,扫描线右端点然后用一个 ds 来维护左端点。假设当前右端点的位置从 \(r-1\) 变成哪里 \(r\),则考虑 \(a_r\) 上一次出现的位置 \(pos\)。容易观察到结论:若有 \(a_i\le \min\limits_{j=pos}^i a_j\) 则这中间所有其他的颜色都可以直接覆盖掉当前颜色,可以一次涂过去。否则这个地方涂到 \(pre\) 位置之后还需要额外再涂色一次。因此想到拿一个线段树或者 BIT 来维护答案,其中:

  • 若当前的情况是 \(a_i\le\min\limits_{j=pos}^ia_j\),则只需要更新 \(\forall j\in[pos+1,i],a_j\leftarrow a_j+1\)
  • 否则,当前右端点到之前的每一个地方都需要额外单独染色一次,更新 \(\forall j\in[1,i],a_j\leftarrow a_j+1\)

区间静态最值用 ST 表简单维护,总时间复杂度为 \(O((n+Q)\log n)\) 可以通过该题。

namespace Loyalty
{
    int tree[N];
    vector<pair<int, int>> Q[N];
    int a[N], lg[N], f[N][20], la[N], pre[N], res[N];
    inline void add(int x, int v) { for (; x < N; x += (x &- x)) tree[x] += v; }
    inline int qry(int x) { int s = 0; for (; x; x -= (x &- x)) s += tree[x]; return s; }
    inline int qmin(int l, int r) { int k = lg[r - l + 1]; return min(f[l][k], f[r - (1 << k) + 1][k]); }
    inline void init() {}
    inline void main([[maybe_unused]] int _ca, [[maybe_unused]] int _atc)
    {
        int n, q;
        cin >> n >> q;
        for (int i = 1; i <= n; ++i)
        {
            int x;
            cin >> x;
            add(i, x), add(i + 1, -x);
        }
        while (q--)
        {
            int o;
            cin >> o;
            if (o == 1)
            {
                int l, r, k;
                cin >> l >> r >> k;
                add(l, k), add(r + 1, -k);
            }
            else
            {
                int x;
                cin >> x;
                cout << qry(x) << '\n';
            }
        }
    }
}
posted @ 2026-02-02 00:42  0103abc  阅读(4)  评论(0)    收藏  举报