小P的祕籍 ZZULIOJ - 1523 二分 ST表

題解

類似於括號匹配,將K看爲1將A看爲-1做前綴和記爲a。如果某個區間[l, r]合法則a[l - 1]爲當前區間內的a的最小值、a[r]爲區間內的a最大值。
使用單調棧求出兩個數組l、r,l表示當前位置向左第一個大於當前a的位置、r表示當前位置向右第一個小於當前a的位置。
枚舉答案區間的左端點i二分右端點,二分範圍爲[i, r[i] - 1],右端點如果爲r[i]則非法而小於這個值對於從左向右來說都是合法的。
使用ST表維護l數組的區間最小值,每次二分的時候檢測[m + 1, R]是否有滿足條件的右端點,如果有則放棄左區間向右尋找。
最後二分到一個點的時候停止,當前點爲最右的可行右端點計算答案。

AC代碼

#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
char s[N];
int a[N], l[N], r[N]; //l向左第一個大於i的位置 r向右第一個小於i的位置
int lg2[N], st[20][N];

void GetST(int n)
{
	lg2[0] = -1;
	for (int i = 1; i < N; ++i)
		lg2[i] = lg2[i >> 1] + 1;
	for (int i = 1; i <= n; ++i)
		st[0][i] = l[i];
	for (int i = 1; i <= lg2[n]; ++i)
		for (int j = 1; j + (1 << i) - 1 <= n; ++j)
			st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
}
int Ask(int l, int r)
{
	int w = lg2[r - l + 1];
	return min(st[w][l], st[w][r - (1 << w) + 1]);
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n;
	cin >> n;
	scanf("%s", s + 1);
	for (int i = 1; i <= n; ++i)
		a[i] = a[i - 1] + 1 - 2 * (s[i] == 'A');
	stack<int> stk; //位置
	stk.push(0); //r需要計算0位置
	for (int i = 1; i <= n; ++i)
	{
		while (!stk.empty() && a[i] < a[stk.top()])
			r[stk.top()] = i, stk.pop();
		stk.push(i);
	}
	while (!stk.empty())
		r[stk.top()] = n + 1, stk.pop();
	for (int i = n; i >= 1; --i)
	{
		while (!stk.empty() && a[i] > a[stk.top()])
			l[stk.top()] = i, stk.pop();
		stk.push(i);
	}
	while (!stk.empty())
		l[stk.top()] = 0, stk.pop();
	GetST(n); //st表維護l數組區間最小值 表示範圍內是否有滿足條件的右端點
	int ans = 0;
	for (int i = 1; i <= n; ++i) //枚舉左端點 a[l-1]爲區間最小值a[r]爲區間最大值則滿足條件
	{
		int L = i, R = r[i - 1] - 1; //二分右端點 找到最靠右的滿足條件的右端點
		while (L < R)
		{
			int M = L + R >> 1;
			int res = Ask(M + 1, R);
			if (res < i) //右側能找到位置p且[i, p]內a[p]爲最大值 滿足條件
				L = M + 1; //拋棄左側 儘量靠右
			else
				R = M; //右側沒有滿足的直接拋棄
		}
		ans = max(ans, R - i + 1); //最終爲最右右端點 用R
	}
	cout << ans << endl;

	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章