Loading

题解 【CF803G Periodic RMQ Problem】

\(\Large\texttt{CF803G}\)

标签:线段树、离散化

可能就是码量大了一点

题意

这么清楚了就不作赘述了QwQ

就是要注意它的 \(\texttt{q}\) 大小只有 \(1e5\)

思路

面对这种题目,串的长度很长但是有规律,区间修改为赋值操作,基本上都有一个十分套路的通解,因为对于 \(1e5\) 的操作数据访问很少,操作只要记录两个端点,所以有些元素根本不会用到它,把它们忽视掉就好了,可以用离散化优化。

但是这里的串初始化时一段串重复多次形成的,可能有点难离散化,我的方法是把所有操作两个端点离散化下来,对于每两个端点间的区间(不包括这两个端点)也离散化成一个值,这个值就是区间中最小的数。

这个区间的值可以分类得到:

  1. 区间的两个端点距离超过一个序列 \(a\) 的长度,那么直接将这个区间的值取为序列a的最小值。

  2. 区间的两个端点在完整的一个 \(a\) 序列内,这里可以把它们相对于a序列的位置求出来,用 \(\texttt{ST}\) 表求出区间最小值。

  3. 区间的两个端点跨越了一个序列 \(a\) ,这可以把两端区间(端点 \(r\) 到序列头,端点 \(l\) 序列尾)的最小值,同样用 \(\texttt{ST}\) 表做到 \(O(1)\) 查询。

然后维护好所有的端点后,剩下的操作用朴素的线段树就可以过去。

复杂度 \(\texttt{O(玄学+可过)}\)

目前的最优解 \(3.37ms\)

代码


#include <bits/stdc++.h>
using namespace std;

#define ls n << 1
#define rs n << 1 | 1
const int N = 1e5;
inline int read()
{
    register int s = 0;
    register bool neg = 0;
    register char c = getchar();
    for (; c < '0' || c > '9'; c = getchar())
        neg |= (c == '-');
    for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
        ;
    return (neg ? -s : s);
}

int a, b, c, s[N + 10], p[(N << 1) + 10], top, q[(N << 2) + 10], id, f[N + 10][30];
struct ask
{
    int opt, l, r, x;
} ask[N + 10];
struct Stree
{
    int l, r, mn;
    bool lazy;
} t[(N << 4) + 10];
struct reel_id
{
    int id, val;
} o[(N << 1) + 10];

inline int find(int n)
{
    int l = 1, r = top;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (o[mid].val == n)
            return o[mid].id;
        if (o[mid].val < n)
            l = mid + 1;
        else
            r = mid - 1;
    }
}

inline int query(int l, int r)//ST表O(1)询问
{
    int k = log2(r - l + 1);
    return min(f[l][k], f[r - (1 << k) + 1][k]);
}

inline void up(int n)
{
    t[n].mn = min(t[ls].mn, t[rs].mn);
}

inline void down(int n)
{
    if (!t[n].lazy)
        return;
    t[ls].mn = t[rs].mn = t[n].mn;
    t[ls].lazy = t[rs].lazy = 1;
    t[n].lazy = 0;
}

inline void build(int n, int l, int r)
{
    t[n].l = l;
    t[n].r = r;
    t[n].mn = 1e9;
    if (l == r)
    {
        t[n].mn = q[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    up(n);
}

inline void change(int n, int l, int r, int k)
{
    if (l <= t[n].l && t[n].r <= r)
    {
        t[n].mn = k;
        t[n].lazy = 1;
        return;
    }
    down(n);
    int mid = (t[n].l + t[n].r) >> 1;
    if (l <= mid)
        change(ls, l, r, k);
    if (mid < r)
        change(rs, l, r, k);
    up(n);
}

inline int Query(int n, int l, int r)
{
    if (l <= t[n].l && t[n].r <= r)
        return t[n].mn;
    int mid = (t[n].l + t[n].r) >> 1, sum = 1e9;
    down(n);
    if (l <= mid)
        sum = min(sum, Query(ls, l, r));
    if (mid < r)
        sum = min(sum, Query(rs, l, r));
    return sum;
}

signed main()
{
    a = read();
    b = read();
    int mn = 1e9;
    for (int i = 1; i <= a; i++)
        s[i] = f[i][0] = read(), mn = min(mn, s[i]);
    for (int i = 1; i <= 25; i++)
        for (int j = 1; j + (1 << i) - 1 <= a; j++)
            f[j][i] = min(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
    c = read();
    for (int i = 1; i <= c; i++)
    {
        ask[i].opt = read();
        ask[i].l = read();
        ask[i].r = read();
        if (ask[i].opt == 1)
            ask[i].x = read();
        p[++top] = ask[i].l;
        p[++top] = ask[i].r;
    }
    sort(p + 1, p + top + 1);//离散下询问的所有端点 
    top = unique(p + 1, p + top + 1) - p - 1;
    for (int i = 1; i <= top; i++)
    {
        if (i > 1 && p[i] - p[i - 1] > 1)//离散下区间 
        {
            int l = p[i - 1] + 1, r = p[i] - 1;
            if (r - l + 1 >= a)
                q[++id] = mn;
            else
            {
                l = (l - 1) % a + 1;
                r = (r - 1) % a + 1;
                if (l <= r)
                    q[++id] = query(l, r);
                else
                    q[++id] = min(query(1, r), query(l, a));
            }
        }
        q[++id] = s[(p[i] - 1) % a + 1];
        o[i].val = p[i];
        o[i].id = id;
    }
    for (int i = 1; i <= c; i++)
        ask[i].l = find(ask[i].l), ask[i].r = find(ask[i].r);//修改下操作的两个端点 
    build(1, 1, id);
    for (int i = 1; i <= c; i++)
    {
        if (ask[i].opt == 1)
            change(1, ask[i].l, ask[i].r, ask[i].x);
        if (ask[i].opt == 2)
            printf("%d\n", Query(1, ask[i].l, ask[i].r));
    }
    return 0;
}
posted @ 2020-10-14 16:20  RedreamMer  阅读(172)  评论(0)    收藏  举报