st表 / 线段树
- 若查询去掉 \([l,r]\) 操作后,整个操作过程中出现的值有哪些,由于每次操作值都是 +1 或 -1,因此是连续的,只要求出整个操作过程中出现的最大值和最小值即可
- 求出前缀和, 因为 l 可能为 1,r 可能为 n,所以令 \(s[0]=0,\;s[n+1]=s[n]\)
- 若去掉了 \([l,r]\) 的操作,则 \([0,l-1]\) 中的最大值可能是整个操作的最大值,\([r+1,n+1]\) 的最大值 - \((s[r]-s[l-1])\) 也可能是最大值, 取两者的最大值,可用 st 表 或线段树维护区间最值 (注意线段树不要返回 pair 类型来同时返回最大值,最小值,常数太大会 TLE)
- 最小值同理
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, M = 18;
int f[N][M], g[N][M], s[N];
int n, m;
string str;
void init()
{
for (int j = 0; j < M; j++)
for (int i = 0; i + (1 << j) - 1 <= n + 1; i++)
if (!j)
f[i][j] = s[i];
else
f[i][j] = max(f[i][j-1], f[i + (1 << j-1)][j-1]);
for (int j = 0; j < M; j++)
for (int i = 0; i + (1 << j) - 1 <= n + 1; i++)
if (!j)
g[i][j] = s[i];
else
g[i][j] = min(g[i][j-1], g[i + (1 << j-1)][j-1]);
}
int query_max(int l, int r)
{
int len = r - l + 1;
int k = log(len) / log(2);
return max(f[l][k], f[r - (1<<k) + 1][k]);
}
int query_min(int l, int r)
{
int len = r - l + 1;
int k = log(len) / log(2);
return min(g[l][k], g[r - (1<<k) + 1][k]);
}
int main()
{
int T;
cin >> T;
while(T--)
{
scanf("%d%d", &n, &m);
cin >> str;
str = " " + str;
for (int i = 1; i <= n; i++)
s[i] = s[i-1] + (str[i] == '+' ? 1 : -1);
s[n + 1] = s[n];
init();
while(m--)
{
int l, r;
scanf("%d%d", &l, &r);
int sub = s[r] - s[l-1];
int l1 = query_min(0, l - 1), r1 = query_max(0, l - 1);
int l2 = query_min(r + 1, n + 1) - sub, r2 = query_max(r + 1, n + 1) - sub;
int ans = 0;
if (max(l1, l2) > min(r1, r2))
ans = r1 - l1 + 1 + r2 - l2 + 1;
else
ans = max(r1, r2) - min(l1, l2) + 1;
printf("%d\n", ans);
}
}
return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
const int INF = 1e9;
int n, m;
string str;
int s[N];
struct Node
{
int l, r;
int mx, mn;
}tr[N * 4];
void pushup(int u)
{
tr[u].mn = min(tr[u << 1].mn, tr[u << 1 | 1].mn);
tr[u].mx = max(tr[u << 1].mx, tr[u << 1 | 1].mx);
}
void build(int u, int l, int r)
{
if (l == r)
{
tr[u] = {l, r, s[l], s[l]};
return;
}
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
int query_max(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r)
return tr[u].mx;
int mid = tr[u].l + tr[u].r >> 1;
int maxn = -INF;
if (l <= mid)
maxn = query_max(u << 1, l, r);
if (r > mid)
maxn = max(maxn, query_max(u << 1 | 1, l, r));
return maxn;
}
int query_min(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r)
return tr[u].mn;
int mid = tr[u].l + tr[u].r >> 1;
int minn = INF;
if (l <= mid)
minn = query_min(u << 1, l, r);
if (r > mid)
minn = min(minn, query_min(u << 1 | 1, l, r));
return minn;
}
int main()
{
int T;
cin >> T;
while(T--)
{
scanf("%d%d", &n, &m);
cin >> str;
str = " " + str;
for (int i = 1; i <= n; i++)
s[i] = s[i-1] + (str[i] == '+' ? 1 : -1);
s[n + 1] = s[n];
build(1, 0, n + 1);
while(m--)
{
int l, r;
scanf("%d%d", &l, &r);
int sub = s[r] - s[l-1];
int l1 = query_min(1, 0, l - 1), r1 = query_max(1, 0, l - 1);
int l2 = query_min(1, r + 1, n + 1) - sub, r2 = query_max(1, r + 1, n + 1) - sub;
int ans = 0;
if (max(l1, l2) > min(r1, r2))
ans = r1 - l1 + 1 + r2 - l2 + 1;
else
ans = max(r1, r2) - min(l1, l2) + 1;
printf("%d\n", ans);
}
}
return 0;
}