SN祈祷中...

P3514 [POI2011] LIZ-Lollipop

简要题意

给定一个只有 的序列,给出 次询问,每次询问给出一个数 ,查询序列中有没有一个子串和为 ,如果有则输出区间两端点,如果没有输出 NIE

策略分析

本题最重要的条件就是该序列中只含有 ,因为这个条件,我们就可以得到下面这个性质: 可以被表示,则 也一定可以被表示。这个性质很容易被证明。我们很容易发现,一个区间的两端点有三种情况,全为 ,全为 以及一边 一边 。那么当两边存在 时,只需要减掉这个 即可;如果是两个 ,那么把这两个 一起减掉即可。

现在我们知道了上面这个性质,开始考虑非法情况。既然 可以被表示,那么我们只需要找到最大的 就可以了。如果给出的 比我们能找到的最大的数还大,那么这个数是不合法的。

考虑完非法情况以后,我们来考虑如何求解。我们发现,一个数减去 后奇偶性不变,也就是说如果 是奇数,那么我们将永远无法得到偶数,反之亦然,所以这要求我们分类讨论,分别求出最大的奇数和最大的偶数。因为题目告诉我们序列长度 ,也就是说能够被表示出来的数最多有 种。我们可以提前将这些数的左右区间都预处理出来,然后 查询即可。处理这些数的方法是将最大的奇数和偶数分别缩减,每次缩减 并记下左右区间,按照最开始分析的性质来移动左右指针,知道两指针相遇为止。

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
#include <cstdio>
using namespace std;

namespace SHAWN {
const int N = 1e6 + 7;
int a[N];
int n, m, jmax, omax, sum;
struct node { int l, r; }b[N << 1];

inline void _init() {
int jlidx, jridx, olidx, oridx, tmp1, tmp2, tag1, tag2;
jlidx = olidx = 1; jridx = oridx = n; tmp1 = tmp2 = 0;
for (int i = 1; i <= n; ++i) {
tmp1 += a[i];
if (a[i] == 1) { tag1 = i + 1; break; }
}
for (int i = n; i >= 1; --i) {
tmp2 += a[i];
if (a[i] == 1) { tag2 = i - 1; break; }
}
if (sum % 2) {
jmax = sum;
if (tmp1 < tmp2) {
omax = sum - tmp1;
olidx = tag1;
}
else {
omax = sum - tmp2;
oridx = tag2;
}
}
else {
omax = sum;
if (tmp1 < tmp2) {
jmax = sum - tmp1;
jlidx = tag1;
}
else {
jmax = sum - tmp2;
jridx = tag2;
}
}

//----以上处理最大的奇数和偶数以及其左右端点----
//----以下分别缩减奇偶数得到所有数的左右端点----

int otmp = omax, jtmp = jmax;
b[omax] = {olidx, oridx};
b[jmax] = {jlidx, jridx};
while (olidx < oridx && otmp > 0) {
otmp -= 2;
if (a[olidx] == 1) {
if (a[oridx] == 1) { b[otmp] = {++olidx, --oridx}; }
else if (a[oridx] == 2) { b[otmp] = {olidx, --oridx}; }
}
else if (a[olidx] == 2) { b[otmp] = {++olidx, oridx}; }
}
while (jlidx < jridx && jtmp > 0) {
jtmp -= 2;
if (a[jlidx] == 1) {
if (a[jridx] == 1) { b[jtmp] = {++jlidx, --jridx}; }
else if (a[jridx] == 2) { b[jtmp] = {jlidx, --jridx}; }
}
else if (a[jlidx] == 2) { b[jtmp] = {++jlidx, jridx}; }
}
return;
}

int work()
{
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
char c; cin >> c;
if (c == 'T') { a[i] = 2; }
else if (c == 'W') { a[i] = 1; }
sum += a[i];
}
_init();
while (m--) {
int sar; cin >> sar;
if (sar % 2 && sar > jmax) { cout << "NIE\n"; continue; }
else if (!(sar % 2) && sar > omax) { cout << "NIE\n"; continue; }
cout << b[sar].l << ' ' << b[sar].r << '\n';
}
return 0;
}
}

signed int main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
return SHAWN :: work();
}