3.23 总结

T1 Vacation Query

题目的查询要求一段区间内最长连续 1 的个数,对于最长连续……的问题,我们可以使用线段树维护并分三类情况进行区间合并:

  1. 左区间或者右区间的最长连续 1 个数的最大值
  2. 横跨左右两区间的最长连续 1 的个数,也就是左区间的以区间右端点为右端点的最长连续 1 个数与右区间的以区间左端点为左端点的最长连续 1 个数之和。

那么至此,我们就需要维护三个信息:

  1. 区间的最长连续 1 个数
  2. 以区间左端点为左端点的最长连续 1 个数。
  3. 以区间右端点为右端点的最长连续 1 个数。

再看修改操作,需要将区间取反,那么原先最长连续 1 个数就变成了最长连续 0 个数,所以我们需要再维护三个信息:

  1. 区间的最长连续 0 个数
  2. 以区间左端点为左端点的最长连续 0 个数。
  3. 以区间右端点为右端点的最长连续 0 个数。

至此这道题就做完了。

struct dat
{
    int l, r, s, z, ls, rs, lz, rz;
};
struct tag
{
    int cnt;
    tag(int _cnt = 0) {cnt = _cnt;}
    bool operator==(const tag& other) const
    {
        return cnt == other.cnt;
    }
};
dat f(const tag &t, const dat &d)
{
    dat res = d;
    if (t.cnt & 1)
    {
        swap(res.s, res.z);
        swap(res.ls, res.lz);
        swap(res.rs, res.rz);
    }
    return res;
}
tag g(const tag &x, const tag &y)
{
    return tag{x.cnt ^ y.cnt};
}
dat h(const dat &a, const dat &b)
{
    dat res;
    res.l = a.l;
    res.r = b.r;
    res.s = max({a.s, b.s, a.rs + b.ls});
    res.z = max({a.z, b.z, a.rz + b.lz});
    // :NOTE: 这里的 ls 在左区间全是 1 的情况下加上右区间的 ls。
    res.ls = a.ls + (a.ls == a.r - a.l + 1) * b.ls;
    res.rs = b.rs + (b.rs == b.r - b.l + 1) * a.rs;
    res.lz = a.lz + (a.lz == a.r - a.l + 1) * b.lz;
    res.rz = b.rz + (b.rz == b.r - b.l + 1) * a.rz;
    return res;
}
const tag tt;
const dat dd;

int main()
{
    st.init(1, 1, n);
    while (q--)
    {
        if (c == 1)
            st.upd(1, l, r, {1});
        else
            printf("%d\n", st.qry(1, l, r).s);
    }
    return 0;
}

T2 XOR on Segment

区间二进制操作和非二进制操作混合,考虑将数二进制拆分,此时我们的操作就变成了这样:

  1. 操作一,把每个数的每一位加起来(比如 $3+7=(2+1)+(4+2+1)=4+2\times 2+2\times 1$)
  2. 操作二直接将和的每一位分别异或即可。
const int M = 22;
struct dat
{
    int l, r, c[M];
    dat(int _l = 0, int _r = 0, ll _v = 0)
    {
        l = _l, r = _r;
        for (int i = 0; i < M; ++i)
            c[i] = (_v >> 1) & 1;
    }
    ll get()
    {
        ll res = 0;
        for (int i = 0; i < M; ++i)
            res += (1ll << i) * c[i];
        return res;
    }
};
struct tag
{
    ll x;
    tag(ll _x = 0)
    {
        x = _x;
    }
    bool operator==(const tag &other)
    {
        return x == other.x;
    }
};
const tag tt;
const dat dd;
dat f(const tag &t, const dat &d)
{
    dat res;
    res.l = d.l;
    res.r = d.r;
    for (int i = 0; i < M; ++i)
        res.c[i] = (((t.x >> i) & 1) ? d.r - d.l + 1 - d.c[i] : d.c[i]);
    return res;
}
tag g(const tag &x, const tag &y)
{
    return tag{x.x ^ y.x};
}
dat h(const dat &a, const dat &b)
{
    dat res;
    res.l = a.l;
    res.r = b.r;
    for (int i = 0; i < M; ++i)
        res.c[i] = a.c[i] + b.c[i];
    return res;
}

int main()
{
    st.init(1, 1, n);
    while (q--)
        if (op & 1)
            cout << st.qry(1, l, r).get() << "\n";
        else
            st.upd(1, l, r, {k});
    return 0;
}

T3 A Simple Task

由于是对字母排序,数字的范围很小(只有 26 ),考虑采取桶排序的思想,每一次查询先将区间的桶取出来,然后把区间推平重新填充即可。

struct dat
{
    int l, r;
    ll cnt[M];
    dat() { l = 0, r = 0; memset(cnt, 0, sizeof cnt); }
};
struct tag
{
    int x;
    tag(int _x = -1)
    {
        x = _x;
    }
    bool operator==(const tag &other)
    {
        return x == other.x;
    }
};
const tag tt;
const dat dd;
dat f(const tag &t, const dat &d)
{
    if (!~t.x)
        return d;
    dat tmp = d;
    memset(tmp.cnt, 0, sizeof tmp.cnt);
    tmp.cnt[t.x] = tmp.r - tmp.l + 1;
    return tmp;
}
dat h(const dat &a, const dat &b)
{
    dat tmp;
    tmp.l = a.l;
    tmp.r = b.r;
    for (int i = 0; i < M; ++i)
        tmp.cnt[i] = a.cnt[i] + b.cnt[i];
    return tmp;
}
int main()
{
    st.init(1, 1, n);
    while (q--)
    {
        int i, j, k;
        std::cin >> i >> j >> k;
        dat stmp = st.qry(1, i, j);
        for (int x = 0; x < M; ++x)
        {
            int xx = k ? x : M - 1 - x, cnt = stmp.cnt[xx];
            if (!cnt)
                continue;
            st.upd(1, i, i + cnt - 1, xx);
            i += cnt;
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        dat stmp = st.qry(1, i, i);
        for (char _ = 'z'; _ >= 'a'; --_)
            if (stmp.cnt[_ - 'a'])
                std::cout << _;
    }
}
⚠️ 转载请注明出处