特殊矩阵の压缩存储

inoichi_lim

2020-04-04 16:09:32

Personal

矩阵图像如下所示。 $\begin{vmatrix}a_{11}&a_{12}&...&a_{1n}\\a_{21}&a_{22}&...&a_{2n}\\...\\a_{n1}&a_{n2}&...&a_{nn}\end{vmatrix}$ 共$N\times N$个元素。 ### 1. 对称矩阵: - 一个$n$行$n$列对称矩阵图像特点如下所示: 1. $a_{ij}=a_{ji}(i,j\le n)$。 2. 图像可分为上三角和下三角,两个三角图案全等。 下三角($i\ge j$): $\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<j$)的图差不多就不画了。 我们假设存储下三角的一维数组 **(需要$\frac{N\times(N+1)}{2}$的空间,压缩了一半)** 是数组$b$,且数组下标从$1$开始。 那么我们的$a_{ij}$存到$b$数组里的$k$(也是从$1$开始)和$i,j$有以下关系: $$k=\frac{(i-1)*n}{2}+j$$ 第一部分$\frac{(i-1)*n}{2}$指$\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}$的元素个数。 第二部分$j$指第$i$行还有第$j$个。 以存储下三角为例,代码: ```cpp #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. 三角矩阵 三角矩阵分为上三角矩阵和下三角矩阵,这里以**下三角**为例。 - 一个$n$行$n$列**下三角**矩阵图像特点如下所示: - 若$i<j,a_{ij}=c(i,j\le n)$($c$是**常数**)。 $\begin{vmatrix}a_{11}&c&...&c\\a_{21}&a_{22}&...&c\\...\\a_{n1}&a_{n2}&...&a_{nn}\end{vmatrix}$ 存储方法: 和对称矩阵相同,存储下三角,在询问时若询问上三角就直接回答上三角即可。 以存储下三角为例,代码: ```cpp #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; } ``` 那么上三角呢? 我们观察这个矩阵。 $\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,$则$a_{ij}=c$。 否则,$i\le j,$则$k$和$i,j$的关系如下所示: $$k=\frac{(n-i+2+n)\times(i-1)}{2}+j-i+1=\frac{(i-1)\times(2n-i+2)}{2}+j-i+1$$ - 第一部分$\frac{(n-i+2+n)\times(i-1)}{2}$指代$\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_{...}$的个数。 - 第二部分$j-i+1$为$\begin{vmatrix}c&c&...&a_{i,i}&a_{i,i+1}&...&a_{i,j-1}&a_{i,j}\end{vmatrix}$中$i,j$中的个数。 代码: ```cpp #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. 对角矩阵 对角矩阵的特点如下: - 基本所有非$c$元素集结在对角线旁,共$l$条对角线($l\bmod2=1$),称为$l$对角矩阵。 则半带宽$d=\frac{l+1}{2}$。 矩阵如下所示:$\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\times n-2\times d$空间。 - 对于每一条对角线,先补上$0$直到有意义的每一行长度都是$l$。 - Pass掉$1$行前面的$d$个$0$和$n$行最后的$d$个$0$。 - 公式如下所示(~~其实我也没理解,老师还打错了,还讲的好好的~~): $$k=\begin{cases}(i-1)\times l+j-i&|i-j|<d\\c&|i-j|\ge d\end{cases}$$ 代码: ```cpp #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; } ```