返回顶部

Codeforces Round #741 (Div. 2) D2. Two Hundred Twenty One (hard version) (思维,前缀和)


  • 题意:有一长度为\(n\)的字符串,+表示1,-表示-1,字符串的子区间\([l,r]\)的贡献为,\(a_l-a_{l+1}+a_{l+2}-a_{l+3}...\),\(q\)次询问,每次询问一个区间,问最少删去几个位置的字符是的区间贡献为\(0\),输出答案和删去的位置。

  • 题解:首先,假如区间贡献为\(0\)那么肯定什么都不用删。先考虑区间长度为奇数的情况,假设我们删去\(pos\)位置,那么\([pos+1,r]\)这段区间的贡献就会变成原来的相反数,因此,假如原区间的贡献为\(C\),那么我们就要找一个位置\(pos\),使得贡献\([l,pos-1]=[pos+1,r]=\frac{C}{2}\)。区间贡献可以用前缀和来维护,那么\(sum[pos-1]-sum[l-1]=sum[r]-sum[pos]\)。移项得到\(sum[r]+sum[l-1]=sum[pos-1]+sum[pos]\)。所以我们可以用set维护\(sum[i]+sum[i-1]\)的位置,每次二分查找pos。

    这里有两个问题,一是起点如果是偶数,那么所有贡献都会变成相反数,不影响等式两边。

    二是pos一定会在\([l,r]\)中,因为,起点处贡献为\(0\),终点贡献为\(C\),每个字符的贡献为\(\pm 1\),所以中间一定会有\(\frac{C}{2}\)的位置。

  • 代码

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    char s[N];
    int a[N],sum[N];
    
    int main() {
        int _;
        scanf("%d",&_);
        while(_--){
            int n,q;
            scanf("%d %d",&n,&q);
            getchar();
            scanf("%s",s+1);
            for(int i=1;i<=n;++i) a[i]=(s[i]=='+'?1:-1);
            sum[0]=0;
            for(int i=1;i<=n;++i){
                sum[i]=sum[i-1]+(i&1?1:-1)*a[i];
            }
            unordered_map<int,set<int>> pos;
            for(int i=1;i<=n;++i){
                int x=sum[i]+sum[i-1];
                pos[x].insert(i);
            }
            auto find=[&](int l,int r){
                int x=sum[l-1]+sum[r];
                return *pos[x].lower_bound(l);
            };
            while(q--){
                int l,r;
                scanf("%d %d",&l,&r);
                if(sum[r]-sum[l-1]==0) puts("0");
                else if((r-l+1)&1) printf("1\n%d\n",find(l,r));
                else printf("2\n%d %d\n",r,find(l,r-1));
            }
        }
        return 0;
    }
    
posted @ 2021-09-05 21:05  Rayotaku  阅读(43)  评论(0编辑  收藏  举报