题意
你有一个长度为 n n n,且仅包含 1 1 1和 − 1 -1 −1的序列 a a a,即 a [ i ] ∈ { − 1 , 1 } a[i] \in \{-1,1\} a[i]∈{−1,1}
给你一个数 Q Q Q,表示 Q Q Q次询问
每次询问给定两个数 l , r l,r l,r,要找到 [ l , r ] [l,r] [l,r]内最少删除多少个数,使得其余数拼接后满足
∑ i = 1 n ( − 1 ) i − 1 ∗ a [ i ] = 0 \sum_{i=1}^n(-1)^{i-1}*a[i]=0 ∑i=1n(−1)i−1∗a[i]=0 或者 ∑ i = 1 n ( − 1 ) i ∗ a [ i ] = 0 \sum_{i=1}^n(-1)^{i}*a[i]=0 ∑i=1n(−1)i∗a[i]=0
思路
因为要求区间内数的加权和为 0 0 0,所以要么两种条件都符合,要么都不符合
用 s u m [ i ] sum[i] sum[i]表示 a [ i ] a[i] a[i]的前缀加权和
若 s u m [ r ] − s u m [ l − 1 ] = = 0 sum[r] -sum[l-1] == 0 sum[r]−sum[l−1]==0 不用删除数,直接输出即可
否则当区间长度为奇数时,只需要删除一个数(后面 t i p s tips tips为证明),假设删除的数位置为 p o s pos pos
即有 s u m [ r ] − s u m [ p o s ] = = s u m [ p o s − 1 ] − s u m [ l − 1 ] sum[r] - sum[pos] == sum[pos-1] - sum[l-1] sum[r]−sum[pos]==sum[pos−1]−sum[l−1]
移项后即为 s u m [ r ] + s u m [ l − 1 ] = = s u m [ p o s ] + s u m [ p o s − 1 ] sum[r] + sum[l-1] == sum[pos]+sum[pos-1] sum[r]+sum[l−1]==sum[pos]+sum[pos−1] , p o s ∈ [ 1 , n ] pos \in [1,n] pos∈[1,n]
由于区间 [ l , r ] [l,r] [l,r]给定,只需要预处理出所有的 s u m [ p o s ] + s u m [ p o s − 1 ] sum[pos]+sum[pos-1] sum[pos]+sum[pos−1]值的位置即可
当区间长度为偶数时,任意选择一个位置删除,再当作奇数时处理即可
tips
给定区间 [ l , r ] [l,r] [l,r]
如果区间长度为偶数,那么我们一定能找到一个分割点 k k k,使得 s u m [ r ] − s u m [ k ] = = s u m [ k ] − s u m [ l − 1 ] sum[r] - sum[k] == sum[k] - sum[l-1] sum[r]−sum[k]==sum[k]−sum[l−1]
为什么 ? s u m sum sum数组是连续的(每次最多变化1),且区间长度为偶数,那 s u m [ r ] − s u m [ l − 1 ] sum[r]-sum[l-1] sum[r]−sum[l−1]一定是偶数
即一定能找到 ( s u m [ r ] − s u m [ l − 1 ] ) / 2 (sum[r]-sum[l-1])/2 (sum[r]−sum[l−1])/2的位置,以此作为分割即可
Code
//D2
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#pragma GCC optimize(3)
typedef long long ll;
#define INF 0x3f3f3f3f
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;
#define iss ios::sync_with_stdio(false)
#define debug(x) cout << #x << ": " << x << endl;
inline ll read() {
ll s = 0, w = 1;
char ch = getchar();
while (ch < 48 || ch > 57) {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= 48 && ch <= 57)
s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return s * w;
}
int sum[300005];//加权前缀和
char s[300005];
map<int, vector<int>> mp;//mp[i]为sum[pos]+sum[pos-1]==i的所有pos
int main() {
int t = read();
while (t--) {
mp.clear();
int n = read(), Q = read();
scanf("%s", s + 1);
for (int i = 1; i <= n; ++i) {
int op = s[i] == '+' ? 1 : -1;
if (i & 1) op = -op;
sum[i] = sum[i - 1] + op;
}
for (int i = 1; i <= n; ++i) {
mp[sum[i] + sum[i - 1]].push_back(i);
}
while (Q--) {
int l = read(), r = read();
if (sum[r] - sum[l - 1] == 0) {
puts("0");
} else {
if ((r - l + 1) & 1) {
int val = sum[r] + sum[l - 1];
auto it = mp[val];
int pos = lower_bound(it.begin(), it.end(), l)-it.begin();//只需要找到[l,r]内的任意pos
printf("1\n%d\n",it[pos]);
} else {
int val = sum[r - 1] + sum[l - 1];
auto it = mp[val];
int pos = lower_bound(it.begin(), it.end(), l) - it.begin();
printf("2\n%d %d\n",it[pos],r);//偶数随意删除即可,我这里删除最后一个位置
}
}
}
}
}