简略题意
给一个序列,支持三种操作:
- 查询某个位置的值
- 区间赋值
- 不断给某个区间加一个数,直到其中不存在42的幂为止。
n ≤ 1 0 5 , 所 有 输 入 的 数 在 1 0 9 范 围 内 n\leq 10^5,所有输入的数在10^9范围内 n≤105,所有输入的数在109范围内
思路
- 看到这种操作基本上就是个势能分析线段树题
- 构不出太大的数,所以每个数能加的次数就是log次。
- 将每个位置设为他到下一个幂的距离(真实值为next+dis,即dis通常<0),将正数暴力更新掉。
- 操作3暴力做,暴力更新距离,是 O ( 势 能 × log ) O(势能\times\log) O(势能×log)的
- 但是操作2会把势能爆炸,怎么办呢
- 假如当前区间有赋值标记,那么更新正数的时候就一起更新
- 只要将势能函数定义为“叶子”区间的势能和,就可以证明对某个区间的一段连续的操作对势能的影响也是log级别的。
题解的方法更好理解与证明复杂度,但是写起来会更麻烦。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
int t[N], n, q;
ll MI[11];
ll tag[N * 4];
typedef pair<ll, int> S;
S mi[N * 4], same[N * 4];
ll near(ll x) {
return upper_bound(MI + 1, MI + 1 + 10, x) - MI;
}
void build(int x,int l,int r) {
if (l == r) {
int u = near(t[l]);
mi[x] = make_pair(t[l] - MI[u], u);
return;
}
build(x << 1, l, l + r >> 1);
build(x << 1 | 1, (l + r >> 1) + 1, r);
mi[x] = max(mi[x << 1], mi[x << 1 | 1]);
}
void putag(int x, ll v) {
tag[x] += v;
mi[x].first += v;
}
void put_over(int x, S v) {
tag[x] = 0;
same[x] = v;
mi[x] = v;
}
void down(int x) {
if (same[x].first) {
put_over(x << 1, same[x]);
put_over(x << 1 | 1, same[x]);
same[x] = make_pair(0, 0);
}
if (tag[x]) {
putag(x << 1, tag[x]);
putag(x << 1 | 1, tag[x]);
tag[x] = 0;
}
}
void add(int x,int l,int r,int tl,int tr,int v) {
if (tl <= l && r <= tr) {
putag(x, v);
return;
}
if (l > tr || r < tl) return;
down(x);
add(x << 1, l, l + r >> 1, tl, tr, v);
add(x << 1 | 1, (l + r >> 1) + 1, r, tl, tr ,v);
mi[x] = max(mi[x << 1], mi[x << 1 | 1]);
}
ll query(int x,int l,int r,int tg) {
if(l==r)return MI[mi[x].second] + mi[x].first;
down(x);
if(tg<=(l+r>>1)) return query(x<<1,l,l+r>>1,tg);
else return query(x<<1|1,(l+r>>1)+1,r,tg);
}
bool upgrade(int x,int l,int r,int tl,int tr) {
if (l > tr || r < tl || mi[x].first < 0) return 0;
if (same[x].first != 0 && tl <= l && r <= tr || l == r) {
ll v = mi[x].first + MI[mi[x].second];
int deg = near(v);
mi[x] = make_pair(v - MI[deg], deg);
if (same[x].first != 0) {
same[x] = mi[x];
tag[x] = 0;
}
return mi[x].first + MI[mi[x].second] == MI[mi[x].second - 1];
}
down(x);
bool ret = 0;
ret |= upgrade(x << 1, l, l + r >> 1, tl, tr);
ret |= upgrade(x << 1 | 1, (l + r >> 1) + 1, r, tl, tr);
mi[x] = max(mi[x << 1], mi[x << 1 | 1]);
return ret;
}
void set_value(int x,int l,int r,int tl,int tr,S v) {
if (tl <= l && r <= tr) {
put_over(x, v);
return;
}
if (l > tr || r < tl) return;
down(x);
set_value(x << 1, l, l + r >> 1, tl, tr, v);
set_value(x << 1 | 1, (l + r >> 1) + 1, r, tl, tr ,v);
mi[x] = max(mi[x << 1], mi[x << 1 | 1]);
}
int main() {
freopen("e.in","r",stdin);
cin >> n >> q;
for(int i = 1; i <= n; i++) {
scanf("%d", &t[i]);
}
MI[0] = 1, MI[1] = 42;
for(int i = 2; i <= 10; i++) MI[i] = MI[i - 1] * MI[1];
build(1, 1, n);
for(int i = 1; i <= q; i++) {
int ty; scanf("%d", &ty);
if (ty == 1) {
int x; scanf("%d", &x);
printf("%lld\n", query(1, 1, n, x));
} else {
int l,r,v; scanf("%d %d %d",&l,&r,&v);
if (ty == 3) {
while (1) {
add(1, 1, n, l, r, v);
if(!upgrade(1, 1, n, l, r)) break;
}
} else {
int u = near(v);
set_value(1, 1, n, l, r, make_pair(v - MI[u], u));
}
}
}
}