1. 程式人生 > >練習】k倍區間(思維,陣列)

練習】k倍區間(思維,陣列)

題幹:

問題描述

  給定一個長度為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

解題報告:

    首先看到連續區間想到字首和,看到資料範圍想想,可否滿足某個遞推關係,即:知道了某一個區間是k倍區間,相應的可以推出另外的區間也是k倍區間。或者,預處理出某些值,方便得到我們想要的結果。

    可以想出,mod k  同餘的兩段區間合併起來肯定是%k==0的,所以我們預處理出字首和的餘數。程式碼就不難寫了。

AC程式碼:(大一的程式碼了、、、懷念逝去的時光啊!順便吐槽一下當時寫程式碼的稚嫩)

#include<iostream>
using namespace std;
int yu[1000005];
int a[1000005];
int main()
{
	int n,k;
	long long int cnt=0;//用int有一個案例過不了 
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		a[i]=(a[i]+a[i-1])%k;			//一般的字首和	a陣列 要開longlong,因為他是累加的和啊!可能很大呢!這也給我們個提示,需要在這裡同時進行求餘 
		yu[a[i]]++;	//這也是為什麼i從1開始而不從0開始的原因、 
	}
	for(int i=0;i<k;i++){
		//cnt+=(  yu[i] * (yu[i]-1))/2;					//cnt+=(yu[i]*yu[i-1])/2;
	 	cnt+=(  (yu[i]/2.0)  *  (  (yu[i]-1)/2.0  ))*2;							//cnt+=(  (yu[i]/2)  *  (  (yu[i]-1)/2  ))*2;	
	}
	cnt+=yu[0];
	printf("%lld",cnt);
	return 0 ; 
 } 
//5 2 
//1 
//2 
//3 
//4 
//5

AC程式碼2:(標解)

#include<iostream>
using namespace std;
typedef long long ll;
ll bk[100010] = {0};
ll arr[1000010];
int main()
{   
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i = 0; i < n; i++)
        scanf("%lld",&arr[i]);

    arr[0] %= k;
    ll sum = 0;

    for(int i = 1; i < n; i++)
        arr[i] = (arr[i]+arr[i-1])%k;

    for(int i = 0; i < n; i++)
        sum += (bk[ arr[i] ]++);

    printf("%lld",sum+bk[0]);
    return 0;
}