题意:
本题涉及到 区间修改 单点修改 单点查询,因此需要 pushdown
函数,又由于没有 区间查询,因此无需 pushup
。
本题需要使用 带懒标记的线段树,想看讲解可以戳这:带懒标记的线段树讲解。
思路:
因为有向 序列头部和尾部插入操作,所以我们要在 初始化 上做一下手脚,总共有 1e5
个数,1e5
个操作询问,即可能为 1e5
个向前插入 或者 1e5
个向后插入。
因此我们总共开 3e5 * 4
的空间(此时 存储线段树的结构体数组 t
则要开 3e5 * 4 * 4
的空间),从 1e5 + 1
到 1e5 + n
为 初始序列 位置。
用 st
和 ed
标记分别表示 当前头尾,为了维护时候考虑到 先加后乘 和 先乘后加 等顺序问题时,我们需要 推一下公式:
设定 两个懒标记 mul
(初始化为 1
)和 add
(初始化为 0
),
-
如果此时操作为 序列加
X
(modify
函数op==2
) 则add += X
即可, -
如果此时操作为 序列乘
X
(modify
函数op==1
),则为add *= X
,mul *= X
,因为真实数为(a + add)
,此时乘X
则可以变为a * X + add * X
。
当然,在 pushdown
操作时候也需要注意到,当 mul
标记 向下传递 时候,下层原有的 add
,也应该 *= root.mul
void pushdown(int u) //标记下传(注意只对标记操作)
{
auto &root = t[u], &left = t[u<<1], &right = t[u<<1|1];
if(root.mul != 1 || root.add)
{
// 对于左儿子 left 的 mul,直接累乘取模即可
left.mul = left.mul * root.mul % mod;
// 对于左儿子 left 的 add,显然要先在其原有的 add 基础上 乘其父节点 root 传递给它的 mul,之后再加上其父节点 root 传递给它的 add(注意取模)
left.add = (left.add * root.mul % mod + root.add) % mod;
right.mul = right.mul * root.mul % mod;
right.add = (right.add * root.mul % mod + root.add) % mod;
root.mul = 1, root.add = 0;
}
}
由于没有 区间查询,因此无需 pushup
函数,只需要 pushdown
(本题要求实现 单点修改 区间修改 单点查询)
时间复杂度:
O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include<bits/stdc++.h>
using namespace std;
//#define int long long
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
typedef map<int, int> mi;
typedef unordered_map<int, int> umi;
typedef vector<ll> vll;
typedef pair<ll, ll> pll;
#define pb push_back
#define pp pop_back
#define x first
#define y second
const int N = 3e5 + 10, M = 1e5 + 10, mod = 1e9 + 7;
int a[N], n, q;
struct node
{
int l, r;
ll val, mul, add;
} t[N<<2];
void build(int u, int l, int r)
{
t[u] = {l, r}, t[u].mul = 1, t[u].add = 0;
if(l==r)
{
t[u].val = a[l];
return ;
}
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
}
void pushdown(int u) //标记下传
{
auto &root = t[u], &left = t[u<<1], &right = t[u<<1|1];
if(root.mul != 1 || root.add)
{
left.mul = left.mul * root.mul % mod;
left.add = (root.mul * left.add % mod + root.add) % mod; //
right.mul = right.mul * root.mul % mod;
right.add = (root.mul * right.add % mod + root.add) % mod;
root.mul = 1, root.add = 0;
}
}
ll ask(int u, ll x) //查询第 x 个元素
{
if(t[u].l==x && t[u].r==x)
{
t[u].val = (t[u].val * t[u].mul % mod + t[u].add) % mod;
t[u].mul = 1, t[u].add = 0; //一定记得这里也要清除标记
return t[u].val;
}
pushdown(u);
int mid = t[u].l + t[u].r >> 1;
if(x<=mid) return ask(u<<1, x);
else return ask(u<<1|1, x);
}
void modify(int u, int l, int r, ll b, int op) //区间修改(区间乘 区间加 先只对标记进行修改处理)
{
if(l<=t[u].l && r>=t[u].r)
{
if(op==1) //区间乘
{
t[u].mul = t[u].mul * b % mod;
t[u].add = t[u].add * b % mod;
}
else t[u].add = (t[u].add + b) % mod; //区间加
return ;
}
pushdown(u);
int mid = t[u].l + t[u].r >> 1;
if(l<=mid) modify(u<<1, l, r, b, op);
if(r>=mid+1) modify(u<<1|1, l, r, b, op);
}
void change(int u, int x, ll v) //单点修改(头尾添加)
{
if(t[u].l==x && t[u].r==x) {t[u].val = v; return ;}
int mid = t[u].l + t[u].r >> 1;
if(x<=mid) change(u<<1, x, v);
else change(u<<1|1, x, v);
}
signed main()
{
int t; //cin>>t;
t = 1;
while(t--)
{
scanf("%d%d", &n, &q);
for(int i=1+M; i<=n+M; ++i) scanf("%d", &a[i]);
int st = 1 + M, ed = n + M;
build(1, 1, N);
while(q--)
{
int op; ll b;
scanf("%d%lld", &op, &b);
if(op==5) printf("%lld\n", ask(1, st + b - 1));
else if(op==1 || op==2)
{
modify(1, st, ed, b, op);
}
else
{
if(op==3) --st, change(1, st, b);
else ++ed, change(1, ed, b);
}
}
}
return 0;
}