题目链接: Two Hundred Twenty One (hard version)
大致题意
给定长度为n, 且仅由’+’, '-'构成的序列, 分别表示这个位置的值为+1, -1.
现在从中取出
[
l
,
r
]
[l, r]
[l,r], 组成长度为
r
−
l
+
1
r - l + 1
r−l+1的新序列
a
a
a. 定义:
C
=
a
1
−
a
2
+
a
3
−
a
4
.
.
.
C = a_1 - a_2 + a_3 - a_4 ...
C=a1−a2+a3−a4...
我们可以任意删除某些位置的数字(删除后, 该位置后的数字会向前补位), 要求删除尽可能少的位置, 使得
C
=
0
C = 0
C=0.
最后输出: 删除的位置数目, 以及具体删除的位置下标.
解题思路
思维
我们考虑 [ l , r ] [l, r] [l,r]如果本身就已经满足要求, 那么我们不需要删除.
反之, 如果 [ l , r ] [l, r] [l,r]为奇数长度区间, 则我们至少需要删除一个位置, 才能满足题意. 若为偶数长度, 则我们至少需要删除两个位置.
最后C = 0, 表明剩余的数字一定得是偶数个. 因此偶数的情况我们至少要删除两次.
对于 [ l , r ] [l, r] [l,r] 删除一个位置 p o s pos pos, 则变化为 [ p o s + 1 , r ] [pos + 1, r] [pos+1,r]区间贡献变为原先的相反数.
我们考虑假设奇数长度区间 [ l , r ] [l, r] [l,r]此时 C = k ( k ≠ 0 ) C = k (k \ne 0) C=k(k=0), 我们应当找到 [ p o s + 1 , r ] [pos + 1, r] [pos+1,r]区间, 使得该区间的贡献为 ⌊ k 2 ⌋ \lfloor \frac k2 \rfloor ⌊2k⌋. 这样我们删除位置 p o s pos pos, 即可使得 C = 0 C = 0 C=0.
对于偶数长度区间 [ l , r ] [l, r] [l,r], 我们可以删除左端点 或 右端点, 转化为奇数长度区间情况即可.
考虑如何找到
p
o
s
pos
pos?
由上述分析, 我们相当于把
[
l
,
r
]
[l, r]
[l,r]在
p
o
s
pos
pos位置分隔开, 使得
[
l
,
p
o
s
−
1
]
[l, pos - 1]
[l,pos−1]与
[
p
o
s
+
1
,
r
]
[pos + 1, r]
[pos+1,r]产生的贡献相互抵消掉.
如果用 s [ ] s[] s[]记录每个位置贡献的前缀和. 则有公式 s [ r ] − s [ p o s ] = s [ p o s − 1 ] − s [ l − 1 ] s[r] - s[pos] = s[pos - 1] - s[l - 1] s[r]−s[pos]=s[pos−1]−s[l−1]. 变形得: s [ l − 1 ] + s [ r ] = s [ p o s − 1 ] + s [ p o s ] s[l - 1] + s[r] = s[pos - 1] + s[pos] s[l−1]+s[r]=s[pos−1]+s[pos].
因此我们记录 s [ i − 1 ] + s [ i ] s[i - 1] + s[i] s[i−1]+s[i]的值即可.
前缀数组 s [ ] s[] s[]记录的是从第一个位置开始的情况, 为什么可以保证公式的正确性呢?
如果当前区间与从1开始的情况不一样. 即: l l l位置是记录的是负号的情况, 而 [ l , r ] [l, r] [l,r]中是正号的情况. 那么前缀和的改变为: 变成原先的相反数, 对于后续的区间同样如此, 就相当于我们在等式两边加上了负号, 是不影响结果的.
p o s pos pos位置一定存在于 [ l , r ] [l, r] [l,r]中吗?
一定. 对于 [ l , r ] [l, r] [l,r], 当我们计算到 r r r位置时, C = k C = k C=k. 由于我们从左向右进行计算时, 每个位置只会改变 ± 1 \pm1 ±1, 而 p o s − 1 pos - 1 pos−1位置应该产生了 ⌊ k 2 ⌋ \lfloor \frac k2 \rfloor ⌊2k⌋的贡献. 因此 p o s ∈ [ l , r ] pos \in [l, r] pos∈[l,r].
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 3E5 + 10;
char a[N];
int s[N];
int main()
{
int t; cin >> t;
while (t--) {
int n, m; scanf("%d %d", &n, &m);
scanf("%s", a + 1);
unordered_map<int, set<int>> st;
rep(i, n) {
s[i] = s[i - 1] + (a[i] == '+' ? 1 : -1) * (i & 1 ? 1 : -1);
int qaq = s[i] + s[i - 1];
st[qaq].insert(i);
}
auto fact = [&](int l, int r) {
int qaq = s[l - 1] + s[r];
return *st[qaq].lower_bound(l);
};
rep(i, m) {
int l, r; scanf("%d %d", &l, &r);
if (s[l - 1] == s[r]) puts("0");
else {
if ((r - l + 1) & 1) printf("1\n%d\n", fact(l, r));
else printf("2\n%d %d\n", r, fact(l, r - 1));
}
}
}
return 0;
}