[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
", l[k], r[k]);
    }
    return 0;
}

值得一提的是,存在性问题中往往可以从一个解进行调整得到另一种解。

因此对于调整的可行性的观察是必不可少的。

GO!
原文地址:https://www.cnblogs.com/Go7338395/p/13849043.html