cf1562D2. Two Hundred Twenty One (hard version)(数学&思维)

收获

题目

  1. D2. Two Hundred Twenty One (hard version)
  2. 题意:给定一个只包含'+''-'且长度为 n n n的字符串, q q q次查询,每次查询为[l,r],要求输出最少需要删除多少个数(删除的数立即合并,补上空隙)满足条件,同时输出删除的这几个数字的下标。
    1. s i s_i si'+' a i a_i ai为1,为'-' a i a_i ai为-1。
    2. 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。——差不多就是这个意思,和题意有出入,但是做法一样。
  3. 题解
    1. 这题还有个简单版本,我很快猜出来了,当为奇数时,一定可以删除一个数就满足条件。
      1. 后来证明:假设删除的数为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的时候。
    2. 所以,题目简化为区间[l,r]删除一个数使cans为0,求删除的数pos,l<=pos<=r。
    3. 然后找出存在的单调性,就可以直接二分了???——然而并没有找到。
    4. 分析删除a[pos]满足条件即求满足sum[r]-sum[pos]=sum[pos-1]-sum[l-1]
      1. 即只需要找到满足条件sum[pos-1]+sum[pos]=sum[r]+sum[l-1]的pos即可。然后这里就需要一个技巧性的操作了,记录所有值为sum[pos-1]-sum[pos]的下标!!1<=pos<=n。
  4. 代码
/*
1. 题目化简为:奇数个数,删去一个数------->0
    我们猜到一定可以删掉一个数然后满足条件。
2. 关键,找到其单调性,然后就可以二分了(忽略这里,这是错误的,应该是找到满足sum[pos-1]+sum[pos]=sum[r]+sum[l-1]的pos就ok了)
*/
#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);  //其实一定存在sum[i]+sum[i-1],i在[l,r]内,因为每次只加2或0或-2,且每次操作都是偶数
    // cout << ">>>>>>" << (sum[r] - sum[*p] - (sum[(*p) - 1] - sum[l - 1]))
        //  << endl;
    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;
}
/*

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值