高斯消元
首先,相比于朴素的高斯消元,高斯约旦消元更好写且答案更容易表达,以下代码实现全部采用这种方式
普通的高斯消元通过加减构造上三角矩阵,约旦消元通过加减构造对角线矩阵
传统方程求解
首先是对于多解及无解的判断
如果消元过程中某一列第 \(i\) 行之后都为 \(0\),一定产生一个特殊解
如果是形如 \(0x_i=0\),则有无数组解
如果形如 \(0x_i=a_i\),则无解
以这道题为例附上约旦消元模板
比较基础的应用,钦定哪个方程是错的,然后消元按照题意判断是否满足条件。
一个元素很自然地代表一个未知数
那么可以把最后一个未知数先设为零,消元消元完取反即可
异或方程组
对于异或方程组,操作完全类似,只不过相减变成整行异或,这个一般可以用 \(bitset\) 进行优化
判断用几个条件可以算出结果,相当于判断除去无用方程(即多解)的凑够 \(n\) 个方程的最后位置
将每个位置作为一个未知数,列出上下左右相关方程即可
由于需要输出方案,消成上三角矩阵更为方便
带状矩阵消元
带状矩阵消元可以做到 \(nk^2\) 的复杂度
具体而言暴力消掉对角线下方的东西然后暴力会带即可
这是模板题
模拟题意写出矩阵可以发现是环带状的,多出来两部分东西,其实只有左下角的有影响,那么每一行都对最后 \(k\) 行消即可
具体实现最好使用 \(STL\) 等
代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
//#define int long long
const int maxn=2e4+5;
cc_hash_table <int,long long>a[maxn];
int sta[maxn],tp,l[maxn],r[maxn],n,k;
//#define int long long
#define fi first
#define se second
const int mod=998244353;
long long ans[maxn],sum;
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int po(int a,int b=mod-2){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
void print(){
puts("hhh");
for(int i=1;i<=n;i++){
for(int j=1;j<=n+1;j++)cout<<a[i][j]<<" ";
puts("");
}
}
void gauss(){
for(int i=1;i<=n;i++){
long long w=po(a[i][i]);
for(auto x:a[i])if(!x.se)sta[++tp]=x.fi;
while(tp)a[i].erase(sta[tp--]);
for(auto &x:a[i])x.se=x.se*w%mod;
for(int j=min(n,i+k);j>=i+1;j--){
if(a[j].find(i)==a[j].end())continue;
w=a[j][i];
for(auto x:a[i])a[j][x.fi]=(a[j][x.fi]-w*x.se)%mod;
}
for(int j=max(i+1,n-k);j<=n;j++){
if(a[j].find(i)==a[j].end())continue;
w=a[j][i];if(!w)continue;
for(auto x:a[i])a[j][x.fi]=(a[j][x.fi]-w*x.se)%mod;
}
}
for(int i=n;i>=1;i--){
ans[i]=a[i][n+1];
for(auto x:a[i]){
if(x.fi<=n&&x.fi!=i){
(ans[i]-=x.se*ans[x.fi]%mod)%=mod;
}
}
}
return ;
}
signed main(){
// freopen("ex_seventy3.in","r",stdin);
freopen("seventy.in","r",stdin);
freopen("seventy.out","w",stdout);
n=read(),k=read();
for(int i=1;i<=n;i++){
sum=0;
for(int j=k;j>=1;j--)(sum+=l[j]=read())%=mod;
for(int j=1;j<=k;j++)(sum+=r[j]=read())%=mod;
a[i][i]=1;sum=po(sum);
for(int j=1;j<=k;j++){
((a[(i-j+n-1)%n+1][i]-=l[j]*sum%mod))%=mod;
((a[(i+j-1)%n+1][i]-=r[j]*sum%mod))%=mod;
}
}
for(int i=1;i<=n+1;i++)a[n][i]=1;
// print();
gauss();
for(int i=1;i<=n;i++)printf("%d\n",(ans[i]+mod)%mod);
return 0;
}