[POI2011] LIZ-Lollipop

EternalEpic發表於2024-09-19

一道有意思的思維題。首先我們知道如果能有一段和為 \(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;
}