[POI2011]LIZ-Lollipop
通过观察可以发现,假设一个区间 \([l, r]\) 能表示出 \(x\),那么他左右两端只有三种情况:
-
两端均为 \(1\)
-
一端为 \(1\) 一端为 \(2\)
-
两端均为 \(2\)
那么可以发现的是,我们一定能通过调整构造出 \(x - 2\) 的方案。
所以,\(\forall x\) 若 \(x\) 可被凑成,那么 \(x - 2\) 也可被凑成。
那么如果一个数 \(x\) 还能被凑成就只能是另一种情况了:\(x\) 能被凑成但 \(x + 2\) 不能被凑成。
继续观察可以发现,如果一个数 \(x\) 被凑成的区间 \([l, r]\) 左右都还有数,必然也会出现最开始的三种情况。
因此,如果区间 \([l, r]\) 能凑成数 \(x\) 且区间 \([l, r]\) 不一边靠界,那么 \(x + 2\) 必然也能被凑出。
那么上述的最后一种情况又变简单了,根据上面这条性质,这种情况下 \(x\) 只能由一个前缀或后缀凑成。
因此我们只需要维护前缀和后缀和即可,复杂度 \(O(n)\)。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define dep(i, l, r) for (int i = r; i >= l; --i)
const int N = 2e6 + 5;
char s[N];
int n, m, k, a[N], l[N], r[N], vpre[N], vsuf[N], pre[N], suf[N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int main() {
n = read(), m = read();
scanf("%s", s + 1);
rep(i, 1, n) a[i] = (s[i] == 'T' ? 2 : 1);
rep(i, 1, n) pre[i] = pre[i - 1] + a[i], vpre[pre[i]] = i;
dep(i, 1, n) suf[i] = suf[i + 1] + a[i], vsuf[suf[i]] = i;
dep(i, 1, n * 2) {
if(vpre[i]) l[i] = 1, r[i] = vpre[i];
else if(vsuf[i]) l[i] = vsuf[i], r[i] = n;
else if(l[i + 2]) {
int L = l[i + 2], R = r[i + 2];
if(a[L] == 2) l[i] = L + 1, r[i] = R;
else if(a[R] == 2) l[i] = L, r[i] = R - 1;
else l[i] = L + 1, r[i] = R - 1;
}
}
while (m--) {
k = read();
if(!l[k]) puts("NIE");
else printf("%d %d\n", l[k], r[k]);
}
return 0;
}
值得一提的是,存在性问题中往往可以从一个解进行调整得到另一种解。
因此对于调整的可行性的观察是必不可少的。