[POI2011] LIZ-Lollipop

一道有意思的思维题。首先我们知道如果能有一段和为 \(x\) 那么必然有一段和为 \(x - 2\) ,因为和为 \(x\) 的那一段的首位要么全为1,要么至少有一个2存在。

这样我们求出最大可表示偶数 \(max_0\) 和最大可表示奇数 \(max_1\),由数学归纳法可以知道比 \(max_0\) 小的所有偶数和比 \(max_1\) 小的所有奇数都可以被表示。

现在我们考虑求出一组可行的区间。首先最大的区间就是序列本身,而与他奇偶相异的最大值由数学归纳一定是从左至右或者从右至左的一段最长区间,可以用前缀和枚举求出。那么我们考虑每次让值减2,这样可以不漏的把奇偶性相同的数都枚举到。

const int N = 1e6 + 5;
int n, m, a[N], sum[N], L[N << 1], R[N << 1], mx[2]; char s[N];
inline void chk(int x, int l, int r) {
	if (x > mx[x % 2]) mx[x % 2] = x, L[x] = l, R[x] = r;
}

signed main(void) {
	read(n), read(m); readstr(s + 1);
	for (int i = 1; i <= n; i++)
		sum[i] = sum[i - 1] + (a[i] = (s[i] == 'T' ? 2 : 1));
	
	L[sum[n]] = 1; R[sum[n]] = n; mx[sum[n] % 2] = sum[n];
	for (int i = 1; i < n; i++) chk(sum[i], 1, i), chk(sum[n] - sum[i], i + 1, n); 
	
	int s0 = mx[0], l = L[mx[0]], r = R[mx[0]];
	while (l <= r) {
		if (a[l] + a[r] == 2) l++, r--;
		else if (a[l] == 2) l++;
		else r--;
		s0 -= 2;
		if (s0) L[s0] = l, R[s0] = r;
	}
	
	s0 = mx[1]; l = L[mx[1]]; r = R[mx[1]];
	while (l <= r) {
		if (a[l] + a[r] == 2) l++, r--;
		else if (a[l] == 2) l++;
		else r--;
		s0 -= 2;
		if (s0) L[s0] = l, R[s0] = r;
	}
	
	for (int i = 1, x; i <= m; i++) {
		read(x);
		if (mx[x % 2] < x) puts("NIE");
		else writeln(L[x], ' '), writeln(R[x]);
	}
	//fwrite(pf, 1, o1 - pf, stdout);
	return 0;
}
posted @ 2024-09-19 02:32  EternalEpic  阅读(48)  评论(0编辑  收藏  举报