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 }
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 }