AcWing 1230. K倍区间

原题链接

考察:前缀和

2021.3.6 二刷写出来了,树状数组的题目做过这题就会很有灵感....

错误思路:

       求前缀和,再枚举左右端点,时间复杂度O(n^2),TLE

正确思路:

       端点枚举不能优化.可以考虑推式子.要求的答案是(sum[r]-sum[l-1])%k==0.优化一下就可发现式子变成sum[r]%k==sum[l-1]%k.因此就变成找余数相等的端点.

这里有两种方法:

1.全部计数完后再计算

       统计相同余数的个数.在相同余数里计算取两点的方法数.

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <map>
 4 using namespace std;
 5 const int N = 100010;
 6 typedef long long ll;
 7 map<ll,ll> mp;
 8 ll sum[N];
 9 int main()
10 {
11     int n,k;
12     ll ans=0;
13     scanf("%d%d",&n,&k);
14     mp[0] = 1;
15     for(int i=1;i<=n;i++)
16     {
17         scanf("%lld",&sum[i]);
18         sum[i] += sum[i-1];
19         mp[sum[i]%k]++;
20     }
21     for(auto it:mp)
22         ans+=it.second*(it.second-1)/2;
23     printf("%lld\n",ans);
24     return 0;
25 }
参考y总思路才写出来的版本

 

2.边算边计数

      组合数n个选2个公式也等于1+2+...+n-1.因此可以枚举右端点.在计算右端点余数个数前先看右端点能组成多少个区间

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <map>
 4 using namespace std;
 5 typedef long long ll;
 6 const int N = 100010;
 7 int a[N],cnt[N];
 8 ll sum[N];
 9 int main()
10 {
11     int n,k;
12     long long ans=0;
13     scanf("%d%d",&n,&k);
14     cnt[0] = 1;
15     for(int i=1;i<=n;i++)
16     {
17         scanf("%d",&sum[i]);
18         sum[i] += sum[i-1];
19     }
20     for(int i=1;i<=n;i++)
21     {
22         ans+=cnt[sum[i]%k];
23         cnt[sum[i]%k]++;
24     }
25    // for(auto it:mp)
26      //   ans+=(long long)it.second*(it.second-1)/2;
27     printf("%lld\n",ans);
28     return 0;
29 }
y总的版本

 

posted @ 2021-01-19 00:10  acmloser  阅读(63)  评论(0编辑  收藏  举报