标签:线段树、离散化
可能就是码量大了一点
题意
这么清楚了就不作赘述了QwQ
就是要注意它的 ( exttt{q}) 大小只有 (1e5) 。
思路
面对这种题目,串的长度很长但是有规律,区间修改为赋值操作,基本上都有一个十分套路的通解,因为对于 (1e5) 的操作数据访问很少,操作只要记录两个端点,所以有些元素根本不会用到它,把它们忽视掉就好了,可以用离散化优化。
但是这里的串初始化时一段串重复多次形成的,可能有点难离散化,我的方法是把所有操作两个端点离散化下来,对于每两个端点间的区间(不包括这两个端点)也离散化成一个值,这个值就是区间中最小的数。
这个区间的值可以分类得到:
-
区间的两个端点距离超过一个序列 (a) 的长度,那么直接将这个区间的值取为序列a的最小值。
-
区间的两个端点在完整的一个 (a) 序列内,这里可以把它们相对于a序列的位置求出来,用 ( exttt{ST}) 表求出区间最小值。
-
区间的两个端点跨越了一个序列 (a) ,这可以把两端区间(端点 (r) 到序列头,端点 (l) 序列尾)的最小值,同样用 ( exttt{ST}) 表做到 (O(1)) 查询。
然后维护好所有的端点后,剩下的操作用朴素的线段树就可以过去。
复杂度 ( exttt{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
", Query(1, ask[i].l, ask[i].r));
}
return 0;
}