1. 程式人生 > >【題解】入陣曲 luogu3941 前綴和 壓維

【題解】入陣曲 luogu3941 前綴和 壓維

har 畫出 freopen avs 過去的 XML mcg avl 判斷

丹青千秋釀,一醉解愁腸。

無悔少年枉,只願壯誌狂。

題目

題目描述

小 F 很喜歡數學,但是到了高中以後數學總是考不好。

有一天,他在數學課上發起了呆;他想起了過去的一年。一年前,當他初識算法競賽的 時候,覺得整個世界都煥然一新。這世界上怎麽會有這麽多奇妙的東西?曾經自己覺得難以 解決的問題,被一個又一個算法輕松解決。

小 F 當時暗自覺得,與自己的幼稚相比起來,還有好多要學習的呢。

一年過去了,想想都還有點恍惚。

他至今還能記得,某天晚上聽著入陣曲,激動地睡不著覺,寫題寫到雞鳴時分都興奮不 已。也許,這就是熱血吧。

也就是在那個時候,小 F 學會了矩陣乘法。讓兩個矩陣乘幾次就能算出斐波那契數列的 第 10^{100} 項,真是奇妙無比呢。

不過,小 F 現在可不想手算矩陣乘法——他覺得好麻煩。取而代之的,是一個簡單的小 問題。他寫寫畫畫,畫出了一個 $n×m$的矩陣,每個格子裏都有一個不超過 k 的正整數。

小 F 想問問你,這個矩陣裏有多少個不同的子矩形中的數字之和是 k 的倍數? 如果把一個子矩形用它的左上角和右下角描述為 $(x_1,y_1,x_2,y_2)$,其中$x_1 \le x_2,y_1 \le y_2$ ?:那麽,我們認為兩個子矩形是不同的,當且僅當他們以 $(x_1,y_1,x_2,y_2)$ 表示時不同;也就是 說,只要兩個矩形以 $(x_1,y_1,x_2,y_2)$表示時相同,就認為這兩個矩形是同一個矩形,你應該 在你的答案裏只算一次。

輸入輸出格式

輸入格式:

從標準輸入中讀入數據。

輸入第一行,包含三個正整數 n,m,k;

輸入接下來 n 行,每行包含 m 個正整數,第 i 行第 j 列表示矩陣中第 i 行第 j 列 中所填的正整數 $a_{i,j}$。

輸出格式:

輸出到標準輸出中。

輸入一行一個非負整數,表示你的答案。

樣例

Sample Input

2 3 2  
1 2 1  
2 1 2

Sample Output

6

數據範圍與說明

樣例說明
這些矩形是符合要求的:
(1, 1, 1, 3),(1, 1, 2, 2),(1, 2, 1, 2),(1, 2, 2, 3),(2, 1, 2, 1),(2, 3, 2, 3)

數據範圍
技術分享圖片

題解

分析

這道題上來先來一個 $O(N^4)$ 的暴力,使用前綴和;

但是,我們會發現,在枚舉每個子矩陣時,有的部分是重復計算的

  • i,j枚舉子矩陣的上下邊界,o枚舉我們處理到了第幾列,把i、j行之間,右邊界是第o列的矩陣壓成一個數,之後枚舉統計。

  • 我們要把 i 到 j 的矩陣變成一行,然後就可以做 (K倍區間) 詳細的可以看

鏈接 http://blog.csdn.net/qq_35776409/article/details/78226120

具體原理:

對於任意一段區間[l,r]的和就是$sum[r]-sum[l-1]$.

$(sum[r]-sum[l-1])%k$ 保證了[l,r]這段區間要麽%k等於0 要麽比k小.

等於0這表示了正好是k的倍數 然後通過前綴和相同的數據來判斷出剩下的k的倍數:$(sum[r]-sum[l-1])%k==0$.

變形後就是:$ sum[r]%k == sum[l-1]%k $

代碼

#include<iostream> 
#include<cstring> 
#include<cstdio> 

using namespace std; 
#define MAXN 410 
typedef long long LL; 
int a[MAXN][MAXN],b[MAXN],cnt[1000005]; 
LL sum[MAXN][MAXN]; 
inline void read(int &x){ 
    x=0; int f=1; char c=getchar(); 
    while(c>‘9‘||c<‘0‘){ if(c==‘-‘)f=-1; c=getchar(); } 
    while(c>=‘0‘&&c<=‘9‘){ x=x*10+c-‘0‘; c=getchar(); } x*=f; 
} 

LL Ans = 0; 
int main(){ 
    freopen("rally.in","r",stdin); 
    freopen("rally.out","w",stdout); 
    int n,m,Mod; 
    read(n),read(m),read(Mod); 
    for(int i=1; i<=n; ++i) 
        for(int j=1; j<=m; ++j){ 
            read(a[i][j]); 
            sum[i][j] = (sum[i-1][j] + a[i][j] + sum[i][j-1] - sum[i-1][j-1]); 
            if(sum[i][j] >= Mod) sum[i][j] -= Mod; 
        } 
    for(int i=0; i<n; ++i) 
        for(int j=i+1; j<=n; ++j){ 
            cnt[0] = 1; 
            for(int k=1; k<=m; ++k) { 
                b[k] = (sum[j][k] - sum[i][k] ) % Mod; 
                if(b[k] < 0) b[k] += Mod; 
                Ans += cnt[b[k]]; 
                ++cnt[b[k]]; 
            } 
            for(int k=1; k<=m; ++k) cnt[b[k]] = 0; 
        } 
    printf("%lld\n",Ans); 
    fclose(stdin); fclose(stdout); 
    return 0; 
}
?

【題解】入陣曲 luogu3941 前綴和 壓維