特殊矩阵の压缩存储

矩阵图像如下所示。

a11a12...a1na21a22...a2n...an1an2...ann\begin{vmatrix}a_{11}&a_{12}&...&a_{1n}\\a_{21}&a_{22}&...&a_{2n}\\...\\a_{n1}&a_{n2}&...&a_{nn}\end{vmatrix}

N×NN\times N个元素。

1. 对称矩阵:

  • 一个nnnn列对称矩阵图像特点如下所示:
  1. aij=aji(i,jn)a_{ij}=a_{ji}(i,j\le n)
  2. 图像可分为上三角和下三角,两个三角图案全等。

下三角(iji\ge j):
a11a21a22a31a32a33...an1an2an3...ann\begin{vmatrix}a_{11}\\a_{21}&a_{22}\\a_{31}&a_{32}&a_{33}\\...\\a_{n1}&a_{n2}&a_{n3}&...&a_{nn}\end{vmatrix}

上三角(i<ji<j)的图差不多就不画了。

我们假设存储下三角的一维数组 (需要N×(N+1)2\frac{N\times(N+1)}{2}的空间,压缩了一半) 是数组bb,且数组下标从11开始。

那么我们的aija_{ij}存到bb数组里的kk(也是从11开始)和i,ji,j有以下关系:

k=(i1)n2+jk=\frac{(i-1)*n}{2}+j

第一部分(i1)n2\frac{(i-1)*n}{2}a11a21a22a31a32a33...ai1ai1,2ai1,3...ai1,j1\begin{vmatrix}a_{11}\\a_{21}&a_{22}\\a_{31}&a_{32}&a_{33}\\...\\a_{i1}&a_{i-1,2}&a_{i-1,3}&...&a_{i-1,j-1}\end{vmatrix}的元素个数。

第二部分jj指第ii行还有第jj个。

以存储下三角为例,代码:

#include<bits/stdc++.h>
#define ns "-1"
#define fs(i,x,y,z) for(ll i=x;i<=y;i+=z)
#define ft(i,x,y,z) for(ll i=x;i>=y;i+=z)
#define ll long long
#define ull unsigned long long
#define db double
#define ms(a,b) memset(a,b,sizeof(a))
#define sz(a) sizeof(a)
using namespace std;
const int rw[]={-1,0,1,0,-1,1,-1,1},cl[]={0,1,0,-1,-1,1,1,-1};
const int N=1001,inf=0x7f7f7f7f;
int n,q,a,downsj[N*(N+1)/2];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>q;
	fs(i,1,n,1){
		fs(j,1,n,1){
			cin>>a;
			if(i>=j){
				downsj[i*(i-1)/2+j]=a;
			}
		}
	}
	fs(i,1,q,1){
		int x,y;
		cin>>x>>y;
		if(x<y) swap(x,y);
		cout<<downsj[x*(x-1)/2+y]<<endl;
	}
	return 0;
}

2. 三角矩阵

三角矩阵分为上三角矩阵和下三角矩阵,这里以下三角为例。

  • 一个nnnn下三角矩阵图像特点如下所示:
  • i<j,aij=c(i,jn)i<j,a_{ij}=c(i,j\le n)cc常数)。
    a11c...ca21a22...c...an1an2...ann\begin{vmatrix}a_{11}&c&...&c\\a_{21}&a_{22}&...&c\\...\\a_{n1}&a_{n2}&...&a_{nn}\end{vmatrix}

存储方法:

和对称矩阵相同,存储下三角,在询问时若询问上三角就直接回答上三角即可。

以存储下三角为例,代码:

#include<bits/stdc++.h>
#define ns "-1"
#define fs(i,x,y,z) for(ll i=x;i<=y;i+=z)
#define ft(i,x,y,z) for(ll i=x;i>=y;i+=z)
#define ll long long
#define ull unsigned long long
#define db double
#define ms(a,b) memset(a,b,sizeof(a))
#define sz(a) sizeof(a)
using namespace std;
const int rw[]={-1,0,1,0,-1,1,-1,1},cl[]={0,1,0,-1,-1,1,1,-1};
const int N=1001,inf=0x7f7f7f7f;
int n,q,a,downsj[N*(N+1)/2],c;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>q;
	fs(i,1,n,1){
		fs(j,1,n,1){
			cin>>a;
			if(i>=j){
				downsj[i*(i-1)/2+j]=a;
			}else{
				c=a; 
			}
		}
	}
	fs(i,1,q,1){
		int x,y;
		cin>>x>>y;
		if(x<y) cout<<c<<endl;
		else cout<<downsj[x*(x-1)/2+y]<<endl;
	}
	return 0;
}

那么上三角呢?

我们观察这个矩阵。
a11a12a13...a1nca22a23...a2ncca33...a3n...ccc...ann\begin{vmatrix}a_{11}&a_{12}&a_{13}&...&a_{1n}\\c&a_{22}&a_{23}&...&a_{2n}\\c&c&a_{33}&...&a_{3n}\\...\\c&c&c&...&a_{nn}\end{vmatrix}

会发现,若i>j,i>j,aij=ca_{ij}=c

否则,ij,i\le j,kki,ji,j的关系如下所示:

k=(ni+2+n)×(i1)2+ji+1=(i1)×(2ni+2)2+ji+1k=\frac{(n-i+2+n)\times(i-1)}{2}+j-i+1=\frac{(i-1)\times(2n-i+2)}{2}+j-i+1

  • 第一部分(ni+2+n)×(i1)2\frac{(n-i+2+n)\times(i-1)}{2}指代a11a12a13...a1nca22a23...a2nccai1,3...ai1,n\begin{vmatrix}a_{11}&a_{12}&a_{13}&...&a_{1n}\\c&a_{22}&a_{23}&...&a_{2n}\\c&c&a_{i-1,3}&...&a_{i-1,n}\end{vmatrix}中,a...a_{...}的个数。
  • 第二部分ji+1j-i+1cc...ai,iai,i+1...ai,j1ai,j\begin{vmatrix}c&c&...&a_{i,i}&a_{i,i+1}&...&a_{i,j-1}&a_{i,j}\end{vmatrix}i,ji,j中的个数。

代码:

#include<bits/stdc++.h>
#define ns "-1"
#define fs(i,x,y,z) for(ll i=x;i<=y;i+=z)
#define ft(i,x,y,z) for(ll i=x;i>=y;i+=z)
#define ll long long
#define ull unsigned long long
#define db double
#define ms(a,b) memset(a,b,sizeof(a))
#define sz(a) sizeof(a)
using namespace std;
const int rw[]={-1,0,1,0,-1,1,-1,1},cl[]={0,1,0,-1,-1,1,1,-1};
const int N=1001,inf=0x7f7f7f7f;
int n,q,a,downsj[N*(N+1)/2],c;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>q;
    fs(i,1,n,1){
        fs(j,1,n,1){
            cin>>a;
            if(i>j){
                c=a;
            }else{
                downsj[(2*n-i+2)*(i-1)/2+j-i+1]=a;
            }
        }
    }
    fs(i,1,q,1){
        int x,y;
        cin>>x>>y;
        if(x>y) cout<<c<<endl;
        else cout<<downsj[(2*n-x+2)*(x-1)/2+y-x+1]<<endl;
    }
    return 0;
}

3. 对角矩阵

对角矩阵的特点如下:

  • 基本所有非cc元素集结在对角线旁,共ll条对角线(lmod2=1l\bmod2=1),称为ll对角矩阵。

则半带宽d=l+12d=\frac{l+1}{2}

矩阵如下所示:a11a12...cc...ca21a22...cc...cca32a33a34c...c...cccc...an1,nann\begin{vmatrix}a_{11}&a_{12}&...&c&c&...&c\\a_{21}&a_{22}&...&c&c&...&c\\c&a_{32}&a_{33}&a_{34}&c&...&c\\...\\c&c&c&c&...&a_{n-1,n}&a_{nn}\end{vmatrix}

存储方法:

  • 总共需要l×n2×dl\times n-2\times d空间。
  • 对于每一条对角线,先补上00直到有意义的每一行长度都是ll
  • Pass掉11行前面的dd00nn行最后的dd00
  • 公式如下所示(其实我也没理解,老师还打错了,还讲的好好的):

k={(i1)×l+jiij<dcijdk=\begin{cases}(i-1)\times l+j-i&|i-j|<d\\c&|i-j|\ge d\end{cases}

代码:

#include<bits/stdc++.h>
#define ns "-1"
#define fs(i,x,y,z) for(ll i=x;i<=y;i+=z)
#define ft(i,x,y,z) for(ll i=x;i>=y;i+=z)
#define ll long long
#define ull unsigned long long
#define db double
#define ms(a,b) memset(a,b,sizeof(a))
#define sz(a) sizeof(a)
using namespace std;
const int rw[]={-1,0,1,0,-1,1,-1,1},cl[]={0,1,0,-1,-1,1,1,-1};
const int N=1001,inf=0x7f7f7f7f;
int n,q,a,downsj[N*N-2*N],c,d,l;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>q>>l;d=(l+1)/2;
    fs(i,1,n,1){
        fs(j,1,n,1){
            cin>>a;
            if(abs(i-j)>=d){
                c=a;
            }else{
                downsj[(i-1)*l+j-i]=a;
            }
        }
    }
    fs(i,1,q,1){
        int x,y;
        cin>>x>>y;
        if(abs(x-y)>=d) cout<<c<<endl;
        else cout<<downsj[(x-1)*l+y-x]<<endl;
    }
    return 0;
}