C. Good Subarrays(在线处理) Educational Codeforces Round 93 (Rated for Div. 2)

原题连接:http://codeforces.com/contest/1398/problem/C
在这里插入图片描述
样例:ios

inputCopy
3
3
120
5
11011
6
600005
outputCopy
3
6
1

题意: 给定好子数组的概念:若子数组的元素之和等于元素个数,那么这个子数组就为好子数组。那么给你一个整数序列字符串,判断该序列有多少个好子数组。c++

解题思路: 虽然暴力出奇迹,可是这道题无论你怎么暴力,都会TLE(亲测,留下悔恨的泪水)。那么既然不能暴力求解,咱们确定是有巧方法的,什么巧方法呢?就是在线处理,话说在前面,有点难理解。你可能感到很奇怪,这怎么在线处理?咱们想一想,用子数组总和减去元素个数,若是是为0,那么就是好子数组,若是好子数组中又包含好子数组呢?那么该好子数组是否是能够分为三个好子数组。若是不为0呢?那么咱们是否是能够记录这个状态,咱们继续日后探索的的时候忽然发现子数组总和减去元素个数又为咱们上次记录过的状态。那么这个子数组是否是能够拆成咱们上次记录过的状态子数组和另外一个子数组减元素个数为0的好子数组?(仔细思考这里,很是重要!) 那么咱们层层深刻,若是一个子数组能这样分为咱们已经访问过的状态子数组和一个好子数组,那么不就实如今线处理了吗?由于如今咱们只要遍历一遍数组了,其余的子数组咱们就能够利用当前已经记录的子数组分解得到。那么时间复杂度就大大减少了。OK,那么咱们怎么记录以前状态呢?这里就要使用map容器了,咱们标记全部计算的差值状态,利用sum统计值便可。若以前标记过两次,说明咱们能够拆成两种状况:一个差值为标记过的子数组和一个好子数组。OK,具体看代码。我贴了详细注释,若还不太明白,欢迎评论区留言。web

AC代码:数组

/* *邮箱:unique_powerhouse@qq.com *blog:https://me.csdn.net/hzf0701 *注:文章如有任何问题请私信我或评论区留言,谢谢支持。 * */
#include<bits/stdc++.h> //POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	int t,n;
	string str;
	while(cin>>t){
		while(t--){
			cin>>n>>str;
			ll sum=0,cnt=0;
			map<int,int> p;
			p[0]++;//这个状态是属于好子数组的状态,咱们天然得标记为1.
			rep(i,0,n-1){
				sum+=str[i]-'0';
				cnt+=p[sum-(i+1)];//加上以前出现过的状态。这便是咱们能够分解的好子数组数量。
				p[sum-(i+1)]++;//当前状态出现,叠加1.
			}
			cout<<cnt<<endl;
		}
	}
	return 0;
}