题意 : 给你一个长为n的序列a,有n*(n+1)/2个子区间,问这些子区间里面和为完全平方数的子区间个数。1 <= n <= 100000、0 <= ai <= 10
分析 : 题目的枚举对象是子区间的和,自然想到构造前缀和数组去进行操作(任意子区间的和都能由某两个前缀和做差得到)。由于这里每个元素都是大于或等于 0 的,所以前缀和肯定是单调不减的(由于有 0 的存在,所以不一定是单调递增),接下来从小到大枚举每一个前缀和,对于当前枚举的每一个前缀和 sum(now) 而言,其能与前面已经出现的前缀和构成的最大完全平方数必然不超过本身,即 sum(now) - sum( ? ) == num*num ,移项之后就能得到 sum(now) - num*num == sum( ? ) ,即前面是否有出现过值为 sum(now) - num*num 这样的前缀和,如果有,说明必定能凑成完全平方数 num*num ,而我们枚举 num*num 的上限完全可以限制在 sum(now) 以内。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; int Cnt[maxn]; int main(void) { Cnt[0] = 1; int n, tmp, PreFixSum = 0; long long ans = 0; scanf("%d", &n); for(int i=0; i<n; i++){ scanf("%d", &tmp); PreFixSum += tmp; for(int j=0; j*j<=PreFixSum; j++) if(Cnt[PreFixSum - j*j]) ans += Cnt[PreFixSum - j*j]; Cnt[PreFixSum]++; } printf("%lld\n", ans); return 0; }