吐槽一句:这种题型遇到过好多次
几乎每一次都是枚举区间开头位置计算结果
考虑现在区间是[l,r],考虑加入a[r+1]对这个区间合法性的影响
区 间 长 度 每 增 加 1 就 需 要 一 个 0 进 来 抵 消 区间长度每增加1就需要一个0进来抵消 区间长度每增加1就需要一个0进来抵消
当 数 字 为 1 时 , 加 不 加 这 个 1 无 所 谓 , 加 入 这 个 1 , 区 间 长 度 加 1 , 值 也 加 1 当数字为1时,加不加这个1无所谓,加入这个1,区间长度加1,值也加1 当数字为1时,加不加这个1无所谓,加入这个1,区间长度加1,值也加1
当 数 字 为 2 时 , 如 果 加 入 这 个 2 , 区 间 长 度 加 1 , 值 加 2 , 此 时 需 要 有 0 来 填 充 当数字为2时,如果加入这个2,区间长度加1,值加2,此时需要有0来填充 当数字为2时,如果加入这个2,区间长度加1,值加2,此时需要有0来填充
我 们 把 0 看 作 − 1 , 表 示 加 入 0 对 区 间 的 影 响 是 负 面 的 我们把0看作-1,表示加入0对区间的影响是负面的 我们把0看作−1,表示加入0对区间的影响是负面的
同 理 把 1 看 作 0 , 表 示 加 入 1 对 区 间 是 没 影 响 的 同理把1看作0,表示加入1对区间是没影响的 同理把1看作0,表示加入1对区间是没影响的
把 数 字 x 看 作 x − 1 , 表 示 加 入 数 字 x 需 要 区 间 内 还 有 x − 1 个 0 才 能 抵 消 把数字x看作x-1,表示加入数字x需要区间内还有x-1个0才能抵消 把数字x看作x−1,表示加入数字x需要区间内还有x−1个0才能抵消
此时我们要找的合法区间,就是区间和为0的区间了
剩 下 的 就 简 单 了 , 先 用 m a p 记 录 一 遍 所 有 前 缀 和 剩下的就简单了,先用map记录一遍所有前缀和 剩下的就简单了,先用map记录一遍所有前缀和
比 如 , 令 当 前 位 置 的 前 缀 和 是 x , 就 m a p [ x ] + + 比如,令当前位置的前缀和是x,就map[x]++ 比如,令当前位置的前缀和是x,就map[x]++
接 下 来 枚 举 每 个 位 置 作 为 开 头 , 由 于 区 间 结 尾 大 于 开 头 接下来枚举每个位置作为开头,由于区间结尾大于开头 接下来枚举每个位置作为开头,由于区间结尾大于开头
所 以 计 算 答 案 时 先 m a p [ x ] − − , 因 为 这 个 位 置 之 前 的 前 缀 和 不 能 使 用 ( 区 间 末 尾 > 开 头 ) 所以计算答案时先map[x]--,因为这个位置之前的前缀和不能使用(区间末尾>开头) 所以计算答案时先map[x]−−,因为这个位置之前的前缀和不能使用(区间末尾>开头)
然 后 答 案 加 上 m a p [ x ] , 这 个 m a p [ x ] 表 示 后 面 的 前 缀 和 是 x 的 次 数 然后答案加上map[x],这个map[x]表示后面的前缀和是x的次数 然后答案加上map[x],这个map[x]表示后面的前缀和是x的次数
这 就 是 区 间 和 为 0 的 个 数 这就是区间和为0的个数 这就是区间和为0的个数
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int t,n,sumn[maxn],top;
char a[maxn];
map<int,int>mp;
int main()
{
cin >> t;
while( t-- )
{
mp.clear();
long long ans=0;
scanf("%d%s",&n,a+1);
int qian=0;
for(int i=1;i<=n;i++)
{
int w=a[i]-'0'-1;
qian+=w;
mp[qian]++;
}
qian=0;
ans=mp[0];//前缀和本来就是0的也可以作为答案,可以看作区间开头是0
for(int i=1;i<=n;i++)
{
int w=a[i]-'0'-1;
qian+=w;
mp[qian]--;//先把[1,i]的前缀和消除掉,我们想要的区间结尾要大于i才行
ans+=mp[qian];
}
cout << ans << "\n";
}
}