【THUPC 2023 决赛】 Freshman Dream
题目大意
给定 n×n 的 01 矩阵 A 要求构造同样大小的矩阵 B 要求满足 A×B 与两个矩阵中的元素一一乘起来,而且要求 B 矩阵中恰好有 k 个 1。
数据范围满足 1≤n≤100,0≤k≤n2 且 A 为随机生成。
思路
分析矩阵乘法的定义 (A×B)i,j=k=1∑nAi,k⋅bk,j,可以发现对于 B 矩阵的第 j 行是相对独立的可以单独讨论。
分析题目条件,先不考虑取模,可以列出方程:
⎩⎪⎪⎪⎨⎪⎪⎪⎧A1,1⋅B1,k+A1,2⋅B2,k+⋯+A1,n⋅Bn,k=A1,k⋅B1,kA2,1⋅B1,k+A2,2⋅B2,k+⋯+A2,n⋅Bn,k=A2,k⋅B2,k⋮An,1⋅B1,k+An,2⋅B2,k+⋯+An,n⋅Bn,k=An,k⋅Bn,k
接着进行移项:
⎩⎪⎪⎪⎨⎪⎪⎪⎧A1,1⋅B1,k+A1,2⋅B2,k+⋯+A1,n⋅Bn,k−A1,k⋅B1,k=0A2,1⋅B1,k+A2,2⋅B2,k+⋯+A2,n⋅Bn,k−A2,k⋅B2,k=0⋮An,1⋅B1,k+An,2⋅B2,k+⋯+An,n⋅Bn,k−An,k⋅Bn,k=0
提取公因式:
⎩⎪⎪⎪⎨⎪⎪⎪⎧(A1,1−A1,k)⋅B1,k+A1,2⋅B2,k+⋯+A1,n⋅Bn,k=0A2,1⋅B1,k+(A2,2−A2,k)⋅B2,k+⋯+A2,n⋅Bn,k=0⋮An,1⋅B1,k+An,2⋅B2,k+⋯+(An,n−An,k)⋅Bn,k=0
考虑因为所有的元素都是 0 或者 1,所以可以可以将上面的运算转化为异或。
⎩⎪⎪⎪⎨⎪⎪⎪⎧(A1,1⊕A1,k)⊕B1,k⊕A1,2⊕B2,k⊕⋯⊕A1,n⊕Bn,k=0A2,1⊕B1,k⊕(A2,2⊕A2,k)⊕B2,k⊕⋯⊕A2,n⊕Bn,k=0⋮An,1⊕B1,k⊕An,2⊕B2,k⊕⋯⊕(An,n⊕An,k)⊕Bn,k=0
整理成系数的增广矩阵:
⎣⎢⎢⎢⎡A1,1⊕A1,kA2,1⋮An,1A1,2A2,2⊕A2,k⋮An,2⋯⋯⋱⋯A1,nA2,n⋮An,n⊕An,k00⋮0⎦⎥⎥⎥⎤
写一个线性基(高斯消元也可以)求出方程的解,接着跑一个 DP 求出具体的方案即可。
其中 fi,j 表示是否有前 i 行一共用了 j 个 1 的方案,而 gi,j 则记录了选择的方案。
总共的时间复杂度为 O(n4),但是有一个 bitset 的优化。
#include<iostream>
#include<bitset>
#include<stack>
#include<vector>
#include<cstring>
using namespace std;
const int N=105;
int n,m,top;
bitset<N>A[N],a[N],s[N],g[N][N*N];
bitset<N*N>f[N];
vector<bitset<N> >st;
bool vis[N],ans[N][N];
int read(){int x;cin>>x;return x;}
void insert(bitset<N>a){
for(int i=1;i<=n;i++){
if(!a[i]) continue;
if(s[i].none()){s[i]=a;return;}
a^=s[i];
}
}
void solve(int id){
for(int i=1;i<=n;i++) a[i]=A[i],a[i][i]=A[i][i]^A[i][id],s[i].reset();
for(int i=1;i<=n;i++) insert(a[i]);
st.clear();
for(int i=n;i>=1;i--){
if(s[i].none()) continue;
for(int j=1;j<i;j++) if(s[j][i]) s[j]^=s[i];
}
for(int i=1;i<=n;i++){
if(!s[i].none()) continue;
bitset<N> top;top[i]=1;
for(int j=1;j<=n;j++) if(s[j][i]) top[j]=1;
st.push_back(top);
}
memset(vis,0,sizeof(vis));
for(int xwd=0;xwd<(1<<st.size());xwd++){
bitset<N>top;
for(int i=0;i<st.size();i++) if(xwd&(1<<i)) top^=st[i];
int siz=top.count();
if(vis[siz]) continue;
vis[siz]=1;
for(int i=siz;i<=m;i++) if(!f[id][i]&&f[id-1][i-siz]) f[id][i]=1,g[id][i]=top;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) A[i][j]=read();
f[0][0]=1;
for(int i=1;i<=n;i++) solve(i);
if(!f[n][m]) cout<<-1<<'\n',exit(0);
cout<<1<<'\n';
for(int i=n,p=m;i>=1;i--){
bitset<N> top=g[i][p];
for(int j=1;j<=n;j++) if(top[j]) ans[j][i]=1;
p-=top.count();
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) cout<<ans[i][j]<<' ';
cout<<'\n';
}
return 0;
}