1230. K倍区间

https://www.acwing.com/problem/content/1232

一眼前缀和,但是似乎只能优化到O(N^2),1e5的数据会超时,如果在比赛中到是可以抢一些分
枚举左端点和右端点找出所有的i,j组合,判断组合是否满足%k==0

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int a[N],s[N];
int n,k,ans;
int main()
{
	cin >> n >> k;
	for(int i=1; i<=n; i++)
	{
		scanf("%d",&a[i]);
		s[i]=s[i-1]+a[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			int n=s[j]-s[i-1];
			if(n%k==0)ans++;
		}
	}
	cout << ans << endl;
	return 0;
}

但是实际上从数学上 (s[j]-s[i-1])% k == 0 可以被优化成 s[j] % k == s[i] % k

当j固定时,在1 ~ j之间找到有多少个L满足(s[j] - s[i - 1]) % k == 0
 
这其实就是我们第二层循环的含义,我们将循环条件简单的变一下:
 
当j固定时,在0 ~ j - 1之间找到有多少个L满足(s[j] - s[i]) % k == 0
可转换为:s[j] % k == s[i] % k

即有多少个前缀和%k余数相等

求有多少个前缀和%k的余数相等,显然我们可以在求前缀和的时候提前%k,最后再加上余数为0的个数即可

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
typedef long long ll;
int sum[N],a[N],res[N];
int n,k;
ll ans=0;
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum[i]=(sum[i-1]+a[i])%k;
        ans+=res[sum[i]];
        res[sum[i]]++;
    }
    cout<<ans+res[0]<<endl;
    return 0;
}

 

posted @ 2023-02-22 10:50  风乐  阅读(20)  评论(0)    收藏  举报