Gym - 101350A Sherlock Bones (思维+dp)

版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/82659803

The great dog detective Sherlock Bones is on the verge of a new discovery. But for this problem, he needs the help of his most trusted advisor -you- to help him fetch the answer to this case.

He is given a string of zeros and ones and length N.

Let F(x, y) equal to the number of ones in the string between indices x and yinclusively.

Your task is to help Sherlock Bones find the number of ways to choose indices (i, j, k) such that i < j < ksj is equal to 1, and F(i, j) is equal to F(j, k).

Input

The first line of input is T – the number of test cases.

The first line of each test case is an integer N (3 ≤ N ≤ 2 × 105).

The second line is a string of zeros and ones of length N.

Output

For each test case, output a line containing a single integer- the number of ways to choose indices (i, j, k).

Example

扫描二维码关注公众号,回复: 3458527 查看本文章

Input

3
5
01010
6
101001
7
1101011

Output

2
3
7

题目大意:给你一个01串s,让你找出三个下标i,j,k,使得从i到j的1的个数等于从j到k的1的个数,并且s[j]=1,问有多少种这样的i,j,k。

解法:满足这样的条件的i,j,k,其从i到k的1的个数必定为奇数,因此可以先求出1的个数为奇的区间个数,然后排除一些非法的区间如1,001,100等,即可得到答案。

我们设d[i][j]表示以下标i为左端点并且1的个数为奇或偶的个数(0表示偶,1表示奇),则由d[i-1][j]可以直接得到d[i][j],因此可以在O(n)的时间内求出1的个数为奇数的区间个数,然后减去非法区间的个数即可。

注意全部为0的时候要特判一下,而且要用long long来保存答案。

#define FRER() freopen("i.txt","r",stdin)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=2e5+10;
int n;
char s[N];
int d[N][2];

int idx(char ch)
{
    return ch^48;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(d,0,sizeof d);
        scanf("%d%s",&n,s);
        int x=0;
        for(int j=0; j<n; ++j)
        {
            x^=idx(s[j]);
            ++d[0][x&1];
        }
        for(int i=1; i<n; ++i)
        {
            d[i][0]=d[i-1][0];
            d[i][1]=d[i-1][1];
            if(s[i-1]=='0')--d[i][0];
            else --d[i][1];
            if(s[i-1]=='1')swap(d[i][0],d[i][1]);
        }
        ll ans=0;
        for(int i=0; i<n; ++i)ans+=d[i][1];
        if(ans!=0)
        {
            int L=0,R=n-1;
            while(L<n&&s[L]=='0')++L,--ans;
            while(R>=0&&s[R]=='0')--R,--ans;
            for(int i=L; i<=R; ++i)
            {
                if(s[i]=='0')ans-=2;
                else ans-=1;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

滚动数组写法:

#define FRER() freopen("i.txt","r",stdin)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=2e5+10;
int n;
char s[N];
int d[2][2];

int idx(char ch)
{
    return ch^48;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(d,0,sizeof d);
        ll ans=0;
        scanf("%d%s",&n,s);
        int x=0;
        for(int j=0; j<n; ++j)
        {
            x^=idx(s[j]);
            ++d[0][x&1];
        }
        ans+=d[0][1];
        for(int i=1; i<n; ++i)
        {
            d[i&1][0]=d[(i^1)&1][0];
            d[i&1][1]=d[(i^1)&1][1];
            if(s[i-1]=='0')--d[i&1][0];
            else --d[i&1][1];
            if(s[i-1]=='1')swap(d[i&1][0],d[i&1][1]);
            ans+=d[i&1][1];
        }
        if(ans!=0)
        {
            int L=0,R=n-1;
            while(L<n&&s[L]=='0')++L,--ans;
            while(R>=0&&s[R]=='0')--R,--ans;
            for(int i=L; i<=R; ++i)
            {
                if(s[i]=='0')ans-=2;
                else ans-=1;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a54665sdgf/article/details/82659803