【題解】入陣曲 luogu3941 前綴和 壓維
丹青千秋釀,一醉解愁腸。
無悔少年枉,只願壯誌狂。
題目
題目描述
小 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 前綴和 壓維