kickstart 2020 C Perfect Subarray

Perfect Subarray


题目链接


题目简介

如果一个数组中所有数的和是平方数,那么这个数组称为完美数组。求一个数组的子数组中有多少个是完美数组。

解题思路

  • 前缀和的思想
  • 遍历这个数组 a a a,在遍历到第 x x x个元素时, p r e f i x prefix prefix(前缀和)记录 ∑ i = 0 x a [ i ] \sum_{i=0}^{x}a[i] i=0xa[i],我们现在想求以第 x x x个元素为最后一个元素的子数组是完美数组的数量
  • 如果我们记录下每一个前缀和出现的次数(用 s s s数组, s [ i ] s[i] s[i]表示前缀和为 i i i的子数组出现的数量)
  • 那么以第 x x x个元素为最后一个元素的子数组是完美数组的数量就是 s [ p r e f i x − j ∗ j ] s[prefix-j*j] s[prefixjj] j ≥ 0 , j ∗ j 是 我 们 要 遍 历 的 平 方 数 j \geq 0,j*j是我们要遍历的平方数 j0jj ),为什么是这个式子,原因就是用前缀和为 p r e f i x prefix prefix的子数组减去前缀和为 p r e f i x − j ∗ j prefix-j*j prefixjj的子数组得到的子数组的前缀和为 j ∗ j j*j jj,即平方数。也就是说有多少个前缀和为 p r e f i x − j ∗ j prefix-j*j prefixjj的子数组就有多少个完美子数组。(他们都以第 x x x个元素为最后一个元素)
  • 这时,我们在考虑另外一些问题,前缀和有可能是负数,这样就不能用数组索引了,为了解决这个问题,我们将前缀和加一个偏移量,这个偏移量就等于这个数组中所有负数的和的相反数(s1)。
  • 还有就是 s [ 0 ] s[0] s[0]的初值要设置成1,因为如果 p r e f i x − j ∗ j prefix-j*j prefixjj的值为0就代表它本身就是完美数组,我们也要考虑进去这种情况
  • 我们要遍历的平方数的大小一定会小于这个数组中所有正数之和(s2)

代码

代码中的符号基本与解题思路中的符号 一 一 对应

#include<iostream>
#include<cstring>
#define MAXN 100010
using namespace std;
int T,N,a[MAXN],s[100*MAXN],s1,s2;
long long ans;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	
	cin >> T;
	for(int g=1;g<=T;g++){
		ans = 0;s1=0;s2=0;
		cin >> N;
		for(int i=1;i<=N;i++){
			cin >> a[i];
			if(a[i]<0)s1 -= a[i];
			else s2 += a[i]; 
		}
		int prefix=0;
		s[s1]++;
		for(int i=1;i<=N;i++){
			prefix += a[i];
			for(int j=0;j*j<=s2;j++){
				if(s1+prefix-j*j>=0)
				ans += s[s1+prefix-j*j];
			}
			s[s1+prefix]++;
		} 
		memset(s,0,sizeof(int)*(s1+s2+1));
		cout << "Case #" << g << ": " << ans << "\n";
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值