CodeForces 1562D Two Hundred Twenty One
给定一个长度为 n 的字符串表示一个序列 ai,字符串中只有 '+'
、'-'
,第 i 个字符为 '+'
表示 ai=1,为 '-'
表示 ai=−1。q 组询问,每组询问给定两个整数 l,r(1≤l≤r≤n),将 al∼r 单独取出后,求最少删除多少个数字,使得所成长度为 m 的序列 {bi} 满足 ∑i=1m(−1)i−1bi=0。
D1 仅要求出最少删除多少个数字,D2 需要求出删除哪些数字(多解输出任意一组即可)。
T 组数据,1≤T≤103,1≤n,q≤3⋅105。
先考虑没有多组询问。
窝萌首先发现一个性质,那就是如果有两个连续的相同字符,那么这两个字符对答案无贡献,我们可以直接删掉。那我们一次删掉之后,最后得到的序列只能是正负交替的了。于是我们只要考虑此种情况即可。我们发现如果删完的序列长度为奇数的话,我们只要删除最中间那个数就可以了。我们考虑偶数,如果长度已经为 0,那显然就不用删除了,如果不为 0 的话,考虑到们此相同字符消除一定是两个两个删的,所以至少要删两个数,有考虑到如果随便删一个数,那么序列长度就变成奇数了,所以我们只需要删两个数即可。
于是我们发现,如果是询问系列长度为奇数,那答案最多是 1,如果是偶数,我们可以维护前缀和 Si=∑j=1i(−1)j−1aj,如果 Sr−Sl−1 是 0,那答案就是 0,否则是 2。
这样 D1 就做完了。
我们考虑 D2,我们先考虑奇数的情况,我们假设要删掉的数是第 k 个,那我们有 ∑i=lk−1(−1)i−1ai+(−1)⋅∑i=k+1r(−1)i−1ai=0 也即 Sk−1−Sl−1=Sr−Sk 也即 Sk−1+Sk=Sl−1+Sr,于是我们对每个 Sk−1+Sk 相同的开个 set
,之后对每组询问 ⟨l,r⟩,我们只要在所有 Sk−1+Sk=Sl−1+Sr 数中 lower_bound
出一个在 [l,r] 中的答案即可。
长度为偶数就直接随便删掉最左边或是最右边的数,然后按照奇数的方法做即可。
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
| const int maxn = 300005;
int qzh[maxn]; char s[maxn];
void solve() { int n, q; scanf("%d%d%s", &n, &q, s + 1); std::map<int, std::set<int>> mp; for (int i = 1; i <= n; ++i) { qzh[i] = qzh[i - 1] + (((i & 1) == (s[i] == '+')) ? 1 : -1); mp[qzh[i - 1] + qzh[i]].insert(i); } while (q--) { int l, r; scanf("%d%d", &l, &r); if (qzh[r] - qzh[l - 1]) { if ((r - l) & 1) { puts("2"); writesp(r); r--; } else { puts("1"); } writeln(*mp[qzh[l - 1] + qzh[r]].lower_bound(l)); } else { puts("0"); } } }
int main() { int T; scanf("%d", &T); while (T--) { solve(); } return 0; }
|