CF EDU 102 D - Program

D - Program

st表 / 线段树

  1. 若查询去掉 \([l,r]\) 操作后,整个操作过程中出现的值有哪些,由于每次操作值都是 +1 或 -1,因此是连续的,只要求出整个操作过程中出现的最大值和最小值即可
  2. 求出前缀和, 因为 l 可能为 1,r 可能为 n,所以令 \(s[0]=0,\;s[n+1]=s[n]\)
  3. 若去掉了 \([l,r]\) 的操作,则 \([0,l-1]\) 中的最大值可能是整个操作的最大值,\([r+1,n+1]\) 的最大值 - \((s[r]-s[l-1])\) 也可能是最大值, 取两者的最大值,可用 st 表 或线段树维护区间最值 (注意线段树不要返回 pair 类型来同时返回最大值,最小值,常数太大会 TLE)
  4. 最小值同理
#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;
}
posted @ 2022-05-28 21:07  hzy0227  阅读(16)  评论(0编辑  收藏  举报