[2020-2021 集训队作业]Yet Another Linear Algebra Problem
壹、题目描述 ¶
贰、题解 ¶
◆ 结论 !😃
第一问的答案为 \(\det(X^TX)\),第二问的答案为 \(\det(X^TA)\),其中 \(A_{i,j}\) 表示向量 \(\overrightarrow{v_i}\) 是否有颜色 \(j\).
要想到太难了,只能进行解释并加以理解......
◆ 爆切第一问 ~~~~>_<~~~~
第一问,其实就是求选出 \(m\) 个线性不相关的向量的方案数,可以考虑成,选出 \(m\) 个向量组成一个 \(m\times m\) 的矩阵,其行列式不为零的方案数。
但是这样似乎并不是很好解决,反而将问题复杂化,但是我们来看一看现在的问题是什么?
但是如果你足够熟悉,你会想到 \(\rm Binet-Cauchy\) 定理. 如果我们补上一个东西,那么就是:
由于我们已知 \(1^2\equiv 2^2\equiv 1\pmod 3\),所以,原来不等于 \(0\) 的,后来也不会等于 \(0\),并且刚刚好为 \(1\) !那么,我们只需要算出它的值,得到的这个值就是最后的方案数!所以,我们继续反推:
为什么不是 \(\det(XX^T)\) ?因为乘出来的 \(\det\) 肯定为 \(0\).(它不满秩)
◆ 相似的第二问 😄
第二问比较相似,我们实际上需要满足两个条件 —— 选出来的 \(\overrightarrow{v_i}\) 并不是线性相关 + 颜色不重复。
而颜色不重复,如果我们按照上述定义 \(A\),事实上就是从 \(A\) 中选出的向量也不是线性相关的!
所以,我们将两个乘起来求行列式将会得到什么?
如果不满足第一个条件,那么前面那一项将为 \(0\),如果不满足后面的条件,那么后面那一项将为 \(0\),所以,只有在两个同时不满足条件时,这一项才会有值,但这个 值一定是一个奇数?是的,它将一定会是一个奇数,并且值一定为 \(\pm 1\),在 \(\bmod 2\) 下即为 \(1\).
至于为什么 —— 每个元素中的值只有 \(0\) 或者 \(1\),如果它们是线性不相关的,那么最后一定会被消成一个单位矩阵,其行列式为 \(1\),算上交换行时的变号,便只有可能为 \(\pm 1\) 了。
叁、参考代码 ¶
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define NDEBUG
#include<cassert>
namespace Elaina{
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define mmset(a, b) memset(a, b, sizeof a)
// #define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
template<class T>inline T readin(T x){
x=0; int f=0; char c;
while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
return f? -x: x;
}
template<class T>inline void writc(T x, char s='\n'){
static int fwri_sta[1005], fwri_ed=0;
if(x<0) putchar('-'), x=-x;
do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
putchar(s);
}
}
using namespace Elaina;
const int maxn=500;
int n, m, Mod;
/**
* @warning only available for class int or long long
*/
template<class T>struct matrix{
int n, m;
vector< vector<T> >a;
inline matrix(){ n=m=0; a.clear(); }
inline matrix(const int N, const int M, const T v=0): n(N), m(M){
a=vector< vector<T> >(n);
for(int i=0; i<n; ++i)
a[i]=vector<T>(m, v);
}
inline matrix epsilon(const int N){ // switch
(*this)=matrix(N, N, 0);
for(int i=0; i<n; ++i) a[i][i]=1;
return (*this);
}
inline matrix I(const int N){ // switch
(*this)=matrix<T>(N, N, 1);
return (*this);
}
inline matrix operator *(const matrix rhs) const{ // switch
assert(m==rhs.n); matrix ret(n, rhs.m, 0);
for(int i=0; i<n; ++i) for(int j=0; j<m; ++j) if(a[i][j])
for(int k=0; k<rhs.m; ++k)
ret.a[i][k]=(ret.a[i][k]+1ll*a[i][j]*rhs.a[j][k]%Mod)%Mod;
return ret;
}
inline bool operator ==(const matrix rhs) const{
if(n!=rhs.n || m!=rhs.m) return false;
for(int i=0; i<n; ++i) for(int j=0; j<m; ++j)
if(a[i][j]!=rhs.a[i][j])
return false;
return true;
}
inline matrix qkpow(ll power){
assert(n==m);
matrix<T>ret=(matrix<T>(n, n).epsilon(n)), a=(*this);
for(; power; power>>=1, a=a*a)
if(power&1) ret=ret*a;
return ret;
}
inline bool operator !=(const matrix rhs) const{
return !((*this)==rhs);
}
inline void print() const{
printf("N == %d, M == %d\n", n, m);
for(int i=0; i<n; ++i){
for(int j=0; j<m; ++j)
printf("%5d ", a[i][j]);
Endl;
}
}
inline int det(){
assert(n==m); int ret=1;
vector< vector<T> >Mat=a;
for(int i=0; i<n; ++i){
for(int j=i+1; j<n; ++j) while(Mat[j][i]){
// assert(Mat[j][i]>0);
int t=Mat[i][i]/Mat[j][i];
for(int k=i; k<n; ++k)
Mat[i][k]=(Mat[i][k]+Mod-1ll*Mat[j][k]*t%Mod)%Mod;
swap(Mat[i], Mat[j]), ret=-ret;
}
if(!Mat[i][i]) return 0;
ret=1ll*ret*Mat[i][i]%Mod;
}
return (ret+Mod)%Mod;
}
};
vector<int>v[maxn+5];
int c[maxn+5];
inline void input(){
Mod=readin(1);
if(Mod==1) Mod=3;
n=readin(1), m=readin(1);
if(Mod==3){
for(int i=0; i<n; ++i){
v[i].resize(m);
for(int j=0; j<m; ++j)
v[i][j]=readin(1);
}
}
else{
for(int i=0; i<n; ++i){
v[i].resize(m);
for(int j=0; j<m; ++j)
v[i][j]=readin(1);
c[i]=readin(1);
}
}
}
namespace task1{
matrix<int>X, XT;
inline void launch(){
X=matrix<int>(n, m), XT=matrix<int>(m, n);
for(int i=0; i<n; ++i) X.a[i]=v[i];
for(int j=0; j<n; ++j) for(int i=0; i<m; ++i)
XT.a[i][j]=v[j][i];
writc((XT*X).det());
}
}
namespace task2{
matrix<int>XT, A;
inline void launch(){
XT=matrix<int>(m, n), A=matrix<int>(n, m);
for(int j=0; j<n; ++j) for(int i=0; i<m; ++i)
XT.a[i][j]=v[j][i];
for(int i=0; i<n; ++i) A.a[i][c[i]-1]=1;
writc((XT*A).det());
}
}
signed main(){
input();
if(Mod==3) task1::launch();
else task2::launch();
return 0;
}
肆、关键之处 ¶
两个限制条件均为 “线性不相关”,考虑将其放到两个矩阵中,使用行列式时便可同时考虑到这两种情况。