1. 程式人生 > >101350A Sherlock Bones (思維+dp)

101350A Sherlock Bones (思維+dp)

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

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;
}