暴力分塊矩陣乘法
阿新 • • 發佈:2018-12-31
題引
題解
樸素的演算法 O(4096 * 64 * 4096) = O(1e9) 不用想是超時的。
因為每次矩陣乘法中存在很多重複的計算。
考慮將矩陣進行分塊優化。預處理出每塊的值。
怎麼分塊。考慮對A矩陣的列分塊,和B矩陣的行分塊。因為p是公共的邊,且p <= 64
需要注意到的是 B矩陣中的取值僅有01那麼如果對B矩陣進行分塊的話。考慮每塊8個01串。那麼每一塊的取值為[0,255]
於是我們預處理出A[i][j][0,255]的每種取值。i表示A矩陣的i行。j表示每行的第幾塊數。[0,255]表示當前塊與所有01序列的取值。
A切完一共4096*8塊,每塊都預處理出256種情況,預處理的空間是4096*8*256,時間是4096*8*8*256。
那麼在進行乘的時候就按照分好的塊再去做乘法。時間為 4096 * 4096 * 8
程式碼
#include<bits/stdc++.h> #define rep(i,a,n) for(int i=a;i<=n;i++) using namespace std; const int maxn = 4100; int A[maxn][70],B[maxn][70]; int RA[maxn][10][260],RB[10][maxn]; char str[70]; int n,p,m; inline void debug() { rep(i,0,n-1) { rep(j,0,p-1) printf("%d ",A[i][j]); printf("\n"); } rep(i,0,p-1) { rep(j,0,m-1) printf("%d ",B[i][j]); printf("\n"); } } inline void solve() { memset(A,0,sizeof(A)); memset(B,0,sizeof(B)); memset(RA,0,sizeof(RA)); memset(RB,0,sizeof(RB)); rep(i,0,n-1) rep(j,0,p-1){ scanf("%s",str);int len = strlen(str),temp = 0; rep(k,0,len-1) { if(str[k] >= '0' && str[k] <= '9') temp = temp * 16 + str[k] - '0'; else temp = temp * 16 + str[k] - 'A' + 10; } A[i][j] = temp; } rep(i,0,m-1) { scanf("%s",str); rep(j,0,p-1) B[i][j] = str[j]-'0'; } p = (p-1)/8+1; //將p進行分塊,每8個一塊 rep(i,0,n-1) rep(j,0,p-1) { int base = j * 8; rep(k,0,255) rep(l,0,7) if(k & (1<<l)) RA[i][j][k] += A[i][base+l]; } rep(i,0,p-1) rep(j,0,m-1) { int base = i * 8; rep(l,0,7) RB[i][j] += (B[j][base+l] << l); } int res = 0; rep(i,0,n-1) rep(j,0,p-1) rep(k,0,m-1) { res ^= RA[i][j][RB[j][k]]; } printf("%d\n",res); } int main() { while(~scanf("%d%d%d",&n,&p,&m)) { solve(); //debug(); } return 0; }