收获
题目
- D2. Two Hundred Twenty One (hard version)
- 题意:给定一个只包含
'+'
和'-'
且长度为
n
n
n的字符串,
q
q
q次查询,每次查询为[l,r]
,要求输出最少需要删除多少个数(删除的数立即合并,补上空隙)满足条件,同时输出删除的这几个数字的下标。
-
s
i
s_i
si为
'+'
时
a
i
a_i
ai为1,为'-'
时
a
i
a_i
ai为-1。 -
c
a
n
s
=
∑
i
=
l
r
(
−
1
)
i
a
i
=
0
cans=\sum_{i=l}^{r}(-1)^i a_i=0
cans=∑i=lr(−1)iai=0。——差不多就是这个意思,和题意有出入,但是做法一样。
- 题解:
- 这题还有个简单版本,我很快猜出来了,当为奇数时,一定可以删除一个数就满足条件。
- 后来证明:假设删除的数为a[pos],则cans=(sum[pos-1]-sum[l-1])-(sum[r]-sum[pos]),当pos<r时,删除pos+1,
cans`
变为(sum[pos]-sum[l-1])-(sum[r]-sum[pos+1])增加了sum[pos]+sum[pos+1](值为0或-2或2),而cans,cans`
一定为偶数,所以一定时渐变的,与原来变化方向相反,一定能遇到为0的时候。
- 所以,题目简化为:区间[l,r]删除一个数使cans为0,求删除的数pos,l<=pos<=r。
- 然后找出存在的单调性,就可以直接二分了???——然而并没有找到。
- 分析删除a[pos]满足条件即求满足
sum[r]-sum[pos]=sum[pos-1]-sum[l-1]
。
- 即只需要找到满足条件
sum[pos-1]+sum[pos]=sum[r]+sum[l-1]
的pos即可。然后这里就需要一个技巧性的操作了,记录所有值为sum[pos-1]-sum[pos]的下标!!1<=pos<=n。
- 代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6 + 5;
int n, q, l, r, a[N], sum[N], b[N];
char s[N];
map<int, int> mp;
set<int> st[N];
int cnt;
void init() {
for (int i = 1; i <= cnt; i++) st[i].clear();
cnt = 0;
mp.clear();
}
int getans(int l, int r) {
int k = sum[r] + sum[l - 1];
k=mp[k];
set<int>::iterator p;
p = st[k].lower_bound(
l);
return *p;
}
signed main() {
int T;
cin >> T;
while (T--) {
scanf("%d%d", &n, &q);
scanf("%s", s + 1);
init();
for (int i = 1; i <= n; i++) {
a[i] = (s[i] == '+') ? 1 : -1, a[i] = (i & 1) ? a[i] : -a[i];
sum[i] = sum[i - 1] + a[i];
b[i] = sum[i] + sum[i - 1];
if (!mp[b[i]]) mp[b[i]] = ++cnt, st[cnt].insert(1e9);
st[mp[b[i]]].insert(i);
}
while (q--) {
scanf("%d%d", &l, &r);
if (sum[r] - sum[l - 1] == 0)
printf("%d\n", 0);
else if ((r - l + 1) & 1)
printf("%d %d\n", 1, getans(l, r));
else
printf("%d %d %d\n", 2, l, getans(l + 1, r));
}
}
return 0;
}