2017年藍橋杯B組省賽題-k倍區間n複雜度解法

目錄

1.K倍區間

2.題是什麼?

3.思路

4.知識精煉

5.ac代碼


1.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

2.題是什麼?

    題意明瞭,讓你統計在所給長度1e5的數組中,區間和爲k的倍數的子區間數目

3.思路

    首先,最簡單的思路,預處理前綴和,然後做二重循環對將近n*n個子區間一一求餘判斷這個子區間是否爲0,這樣的複雜度爲O(n^2),會爆.

    其次,我的思路,既然我只是要知道區間和爲k倍數的子區間數目而不是到底有哪些子區間,那我豈不是只需要對這些子區間做一個記憶化,保留下對我後續計算有用的特徵就好了嗎,那麼一個區間的什麼特徵對我後續判斷其它區間區間和是否能被k整除有決定性作用呢?

    tips:(記憶化是什麼? 我的理解:記憶化就是以某種方式存儲你計算過的數據,後面加以利用,優化計算效率)

                                                          自然是這個區間求餘k後的結果!

    現在,我們知道能通過這個特徵去記憶化壓縮數據之後,我們該怎麼寫進算法裏並優化統計方法呢?

    自然是做一個前綴和結果求餘k的數組sum和一個記憶化數組num,記錄之前出現過的所有前綴和求餘k的結果的情況,這樣的話之後當我們統計以a[i]結尾的區間有多少個滿足題意時,num[sum[i]]即爲答案!,爲什麼呢? 因爲你想如果k=5,sum[i]=3,以a[i]爲結尾滿足題意的情況數目不就是前面的sum中爲3的個數嗎,我這一整個區間多了3,那我丟掉某個正好多了3的前綴不就不多了嗎!

      正好就是我們的num[3],爲了維護num數組,每次要num[sum[i]]+=1,將本次情況更新進記憶化數組.

4.知識精煉

    有沒有注意到爲什麼num被叫做記憶化數組?因爲他裏面存的某個數字比如num[2]=3的含義是,前綴和求餘結果爲2的情況當前有3種,他是一種對於過去所有情況的記憶,僅僅根據最核心的特徵進行記憶,這就是記憶化!你,get到feel了嗎?

5.ac代碼

純c語言ac:

#include <stdio.h>

int maxn=1e5+5;
int maxk=1e5+5;
 
int main(){
	int a[maxn];
	int sum[maxn]; //前綴和求餘k結果,sum[i]表示(a[0]+a[1]+...+a[i])%k
	int num[maxk];//記憶化,num[i]=x表示在當前已出現x個和爲i的前綴
	
	int n,k,i;
	scanf("%d%d",&n,&k);
	for(i=0;i<n;i++) scanf("%d",&a[i]);
	//初始化前綴和 
	sum[0]=a[0]%k;
	for(i=1;i<n;i++) sum[i]=(a[i]+sum[i-1])%k;
	//初始化記憶數組
	num[0]=1; //初始狀態應該包含空前綴情況!,對後面的狀態遞推異常重要
	for(i=1;i<maxk;i++) num[i]=0;
	
	long long ans=0; 
	//線性同時記憶化 
	for(i=0;i<n;i++) ans+=num[sum[i]]++; 
	
	printf("%lld\n",ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章