Description:
1<=n<=500000
题解:
这题是吉利营员交流的一个子问题。
线段树维护几个东西:
1.和
2.最大值
3.严格次大值
4.最大值个数
5.min标记
可以用势能分析来分析复杂度。
考虑势能为线段树每个节点所代表区间的不同数的个数和。
一次修改只会增加log个。
对于区间取min,假设取x的min。
x>=max,显然不管。
cmax>x>max,可以直接算答案。
x<<cmax,就需要递归下去。
当x<<cmax时,递归下去势能至少减1。
所以复杂度不会超过O(n log n),常数有点大。
要加读入输出优化。
希望之后搞懂其它的子问题。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int N = 500005;
const ll INF = 1e18;
int n, m, l, r, x, y, num;
ll ans, px;
int pl, pr;
struct node {
int mx, cx, ms;
ll s, bz;
} t[N * 4];
void bin(int i) {
int u = i + i, v = u + 1;
if(t[u].mx < t[v].mx) swap(u, v);
if(t[u].mx == t[v].mx) {
t[i].mx = t[u].mx; t[i].ms = t[u].ms + t[v].ms;
t[i].cx = max(t[u].cx, t[v].cx);
} else {
t[i].mx = t[u].mx; t[i].ms = t[u].ms;
t[i].cx = max(t[u].cx, t[v].mx);
}
t[i].s = t[u].s + t[v].s; t[i].bz = INF;
}
void dg(int i, int x, int y) {
if(t[i].bz >= t[i].mx) return;
if(x == y) {
t[i].mx = t[i].s = t[i].bz;
t[i].cx = 0; t[i].ms = 1;
t[i].bz = INF;
return;
}
if(t[i].bz > t[i].cx) {
t[i].s = (ll) (t[i].bz - t[i].mx) * t[i].ms + t[i].s;
t[i].mx = t[i].bz;
return;
}
int m = x + y >> 1;
t[i + i].bz = min(t[i + i].bz, t[i].bz); dg(i + i, x, m);
t[i + i + 1].bz = min(t[i + i + 1].bz, t[i].bz); dg(i + i + 1, m + 1, y);
bin(i);
}
void add(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x == y) {
t[i].bz = INF; t[i].mx = max(0, t[i].mx - px);
t[i].ms = 1; t[i].s = t[i].mx;
return;
}
int m = x + y >> 1;
t[i + i].bz = min(t[i + i].bz, t[i].bz); dg(i + i, x, m);
t[i + i + 1].bz = min(t[i + i + 1].bz, t[i].bz); dg(i + i + 1, m + 1, y);
add(i + i, x, m); add(i + i + 1, m + 1, y);
bin(i);
}
void ad(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x >= pl && y <= pr) {
t[i].bz = min(t[i].bz, px); dg(i, x, y);
return;
}
int m = x + y >> 1;
t[i + i].bz = min(t[i + i].bz, t[i].bz); dg(i + i, x, m);
t[i + i + 1].bz = min(t[i + i + 1].bz, t[i].bz); dg(i + i + 1, m + 1, y);
ad(i + i, x, m); ad(i + i + 1, m + 1, y);
bin(i);
}
void fi(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x >= pl && y <= pr) {
ans += t[i].s; return;
}
int m = x + y >> 1;
t[i + i].bz = min(t[i + i].bz, t[i].bz); dg(i + i, x, m);
t[i + i + 1].bz = min(t[i + i + 1].bz, t[i].bz); dg(i + i + 1, m + 1, y);
fi(i + i, x, m); fi(i + i + 1, m + 1, y);
bin(i);
}
void read(int &x) {
char c = ' ';
for(; c < '0' || c > '9'; c = getchar());
x = 0; for(; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48;
}
void write(int x) {
if(x == 0) {
putchar('0'); putchar('\n');
return;
}
int d[20]; d[0] = 0;
while(x) d[++ d[0]] = x % 10, x /= 10;
while(d[0]) putchar(d[d[0] --] + 48);
putchar('\n');
}
int main() {
freopen("dream.in", "r", stdin);
freopen("dream.out", "w", stdout);
scanf("%d %d", &n, &m);
fo(i, 1, n * 4) t[i].bz = INF;
fo(i, 1, n) {
read(x);
pl = pr = i; px = -x;
add(1, 1, n);
}
fo(i, 1, m) {
read(num);
if(num == 1) {
read(x); read(y);
x ^= ans; y ^= ans;
px = y; pl = pr = x;
add(1, 1, n);
} else
if(num == 2) {
read(l); read(r); read(x);
l ^= ans; r ^= ans; x ^= ans;
pl = l, pr = r; px = x;
ad(1, 1, n);
} else {
read(l); read(r);
l ^= ans; r ^= ans;
pl = l, pr = r, ans = 0;
fi(1, 1, n);
printf("%lld\n", ans);
}
}
}

本文详细介绍了使用线段树解决特定问题的方法,包括维护多个关键属性如和、最大值、次大值等,并通过势能分析确保算法效率。此外,还提供了完整的代码实现。

被折叠的 条评论
为什么被折叠?



