蓝桥历届试题——k倍区间【朴素做法+2种AC解法】

k倍区间
时间限制:2.0s 内存限制:256.0MB

问题描述
  给定一个长度为N的数列,A1, A2, … AN,如果其中一段连续的子序列Ai, Ai+1, … Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
  你能求出数列中总共有多少个K倍区间吗?
  
输入格式
  第一行包含两个整数N和K。(1 <= N, K <= 100000)
  以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出格式
  输出一个整数,代表K倍区间的数目。
  
样例输入
5 2
1
2
3
4
5
样例输出
6

朴素做法【暴力枚举(TLE)】

#include <bits/stdc++.h> 
using namespace std;
int n,k,ans,cnt,sum[100010];

int main()
{
    
    
	cin>>n>>k;
	sum[0]=0;
	
	for(int i=1; i<=n; i++)
	{
    
    
		int a;
		cin>>a;
		sum[i] = (sum[i-1]+a) %k; //前缀和 
	}  
	
	for(int i=1; i<=n; i++)
	    for(int j=0; j<i; j++)
		{
    
    
			cnt = sum[i]-sum[j];
			if(cnt%k==0)  ans++;
		} 
	
	cout<<ans;
	return 0;
}

思路1:前缀和+组合数求解
在这里插入图片描述

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll N, K, ans=0;
ll sum[100010];
ll cnt[100010];

int main()
{
    
    
	cin>>N>>K;

	memset(cnt, 0, sizeof(cnt));
	sum[0]=0;
	for(int i=1; i<=N; i++)
	{
    
    
		int a;
		cin>>a;
		//前缀和 
		sum[i] = (sum[i-1] + a)%K;
		cnt[sum[i]]++;
	}
	//情况一
	ans += (cnt[0]*(cnt[0]+1))/2; 
	
	//情况二 
	for(int i=1; i<K; i++)
		ans += (cnt[i]*(cnt[i]-1))/2;
	cout<<ans;
	
	return 0;
}

思路2:前缀和+DP求解
我们首先要我们是要求出任意长度的区间和 符合是k的倍数的 区间个数 这样我们很容易得到 (sum[i]-sum[j]) %k == 0
接着我们可以得到 (sum[i]%k-sum[j]%k) %k == 0

因为前缀和都为正整数 所以我们可以推导出 sum[i]%k==sum[j]%k
所以我们只需要找到前缀和对k求余结果相同的两个点 他们之间的区间即为k倍区间。

int main()
{
    
    
    // ios::sync_with_stdio(false);
    scanf("%d%d",&n,&k);
    ll ans=0;
    
    for(int i=1; i<=n; i++)
    {
    
    
        scanf("%d",&a[i]);
        sum[i]=(sum[i-1]+a[i]) %k; //求前缀和对k求余 
        
        if(sum[i]==0) {
    
     //自己也是k倍区间 
            ++dp[sum[i]];
            ans+=(dp[sum[i]]);
        }
        else {
    
      //和前面余数相同的点组成k倍区间 
            ans+=(dp[sum[i]]);
            ++dp[sum[i]];
        }
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Luoxiaobaia/article/details/108743769