Gym - 101498G(Super Subarray )

In this problem, subarray is defined as non-empty sequence of consecutive elements.

We define a subarray as Super Subarray if the summation of all elements in the subarray is divisible by each element in it.

Given an array a of size n, print the number of Super Subarrays in a.

Input

The first line of the input contains a single integer T (1 ≤ T ≤ 100), the number of test cases.

The first line of each test case contains one integer n (1 ≤ n ≤ 2000), the length of array a.

The second line of each test case contains the sequence of elements of the array a1, a2, ..., an (1 ≤ ai ≤ 109), ai is the i-th element of the array a.

Output

For each test case, print a single integer that represents the number of Super Subarrays in a, on a single line.

Example

Input
2
5
1 2 3 4 5
5
2 3 4 3 6
Output
6
6

题意:给出了n个数字组成的序列,问你在这个序列中,有多少个子序列满足这样一个条件:在这一个子序列中所有元素的和能够整除这个子序列中的每一个元素。拿第一个样例拿第二个样例2 3 4 3 6来说,首先,每一个数字本身都是符合条件的,2这个数字的和可以整除2,3可以整除3......除此之外,还有一个满足条件的序列是2 3 4 3:2+3+4+3=12,12可以整除2,3,4。

题解:这题的解法第一反应都是暴力求解,遍历每一个子序列,看是否满足条件,但不用说肯定超时。正确的做法是通过记录前缀和来求每一个子序列的和(这个应该都想得到),然后要知道,一个数可以整除一些数,那这个数一定可以整除这些数的最小公倍数,反之亦然。所以我们只需要求出每个序列所有数字的的最小公倍数即可判断是否满足要求。
接下来再讲一下n个数的最小公倍数求法,首先求两个数的最小公倍数,接着再用这个公倍数去和第三个数求最小公倍数,就是三个数的最小公倍数了,四个数同理,以此类推。那如何求两个数的最小公倍数呢?首先你要知道,两个互质的数(公约数只有1),他们的最小公倍数就是他们的乘积,这个应该很好理解,所以,求两个数最小公倍数的方法就是先求出他们的最大公约数,然后其中一个数除以最大公约数,这时候,这两个数就是互质的了,再将两个数相乘就是他们的最小公倍数了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<cstdlib>
 8 #include<queue>
 9 #include<stack>
10 #include<vector>
11 #include<list>
12 #define ll long long
13 #define pi 3.14159265358979323846
14 using namespace std;
15 const int inf = 0x3f3f3f3f;
16 const ll mod = 1000000007;
17 const ll maxn = 2000*10e9;
18 ll gcd(ll a,ll b)//求最大公约数 
19 {
20     return b==0 ? a : gcd(b,a%b);
21 }
22 
23 ll lcm(ll a,ll b)//求最小公倍数 
24 {
25     return a/gcd(a,b)*b;
26 }
27 
28 int main()
29 {
30     ll t,n,a[2007],sum[2007];
31     cin>>t;
32     while(t--)
33     {
34         scanf("%lld",&n);
35         sum[0] = 0;
36         for(int i=1; i<=n; ++i)
37         {
38             scanf("%lld",&a[i]);
39             sum[i] = sum[i-1] + a[i];//求前缀和 
40         }
41         ll ans=0;
42         for(int i=1; i<=n; ++i)
43         {
44             ll temp = a[i];//初始状态一个数的最小公倍数就是本身 
45             for(int j=i; j<=n; ++j)
46             {
47                 temp = lcm(temp,a[j]);//求子序列的最小公倍数 
48                 if(temp > maxn) break;
49                 //减枝,公倍数超过此题的数据范围,直接break,因为每加一个数,公倍数只会增加或不变,所以后面都可以不用考虑了 
50                 if((sum[j] - sum[i-1]) % temp == 0)//满足条件,ans++; 
51                     ans++;
52             }
53         }
54         printf("%lld\n",ans);
55     }
56     return 0;
57 }

转载于:https://www.cnblogs.com/tuyang1129/p/9141140.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值