A-Matrix Equation_第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南)(重现赛)
自己写一个2维矩阵或者3维矩阵就可以发现对于每一列来说都是独立的,每一列的n个Cij都是都关系的,这就构成了一个n元一次方程组,其实这就是解一下这个方程组,但是他是问的有多少个矩阵,对于这个方程组构造出的矩阵来说,有多少自由元就说明有多少个Cij是可以任意取值的,也就是有1,2两种选择,不是自由元的就只能有1种;自由元是指系数都是0的未知数,自由元的个数就是高斯消元后0行的个数,所以用高斯消元就可以求出来;
又因为有运算是模2的,所以也就可以看成是异或型矩阵,那么运算方式就要稍微的发生以下变化,比如
上述图片来自2020icpc-济南站_琅歌的博客-CSDN博客
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 2e5+100;
const double eps=1e-8;
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int getinv(int a){return qpow(a,mod-2LL);}
int n;
int a[220][220],b[220][220],d[220][220],fac[N];
int gauss()
{
int newline=1;
for(int k=1;k<=n;k++)
{
int p=newline;
for(int i=newline+1;i<=n;i++)
if(d[i][k]>d[p][k]) p=i;
if(d[p][k]==0) continue;
swap(d[newline],d[p]);
for(int i=newline+1;i<=n;i++)
{
if(d[i][k]==0) continue;
for(int j=1;j<=n;j++)
d[i][j]^=d[newline][j];
}
newline++;
}
newline--;
return n-newline;
}
void print()
{
cout<<"--------------------\n";
for(int i=1;i<=n;i++)
{
for(int k=1;k<=n;k++)
cout<<d[i][k]<<" ";
cout<<endl;
}
cout<<"--------------------\n";
}
signed main()
{
//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
fac[0]=1;
for(int i=1;i<=100000;i++) fac[i]=fac[i-1]*2LL%mod;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) cin>>a[i][j],d[i][j]=a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) cin>>b[i][j];
int ans=1;
for(int j=1;j<=n;j++)
{
for(int i=1;i<=n;i++)
for(int k=1;k<=n;k++) d[i][k]=a[i][k];
for(int i=1;i<=n;i++)
d[i][i]=a[i][i]^b[i][j];
//print();
int res=gauss();
//print();
//cout<<res<<endl;
ans=ans*fac[res]%mod;
//cout<<j<<" "<<res<<endl;
//print();
}
cout<<ans<<endl;
system("pause");
return 0;
}
借助这个题又回顾了一下高斯消元的东西,高斯消元其实就是大一学的矩阵的行列变换使得矩阵变为行阶梯型,步骤为:
1.设一变量newline代表进行到了第几行,最外层枚举列号k,内层枚举行i,找到最大的a[i][k]让其作为主元,将第i行与第newline行交换,如果最大的a[i][k]==0说明该变量是自由元,跳过去操作下一列;
2.将主元下面的数都消掉,就是行变换
3.操作结束后常数项/主元系数就是该未知数的解
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 2e5+100;
const double eps=1e-8;
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int sgn(double x)
{
if(fabs(x)<eps) return 0;
else if(x<0) return -1;
else return 1;
}
int getinv(int a){return qpow(a,mod-2LL);}
int n;
double a[55][55];
void gauss()
{
int nl=1;
for(int k=1;k<=n;k++)
{
int p=nl;
for(int i=nl+1;i<=n;i++)
if(a[p][k]<a[i][k]) p=i;
//找一列中最大的数作为主元
if(sgn(a[p][k])==0) continue;
//如果最大的数是0那这个变量是一个自由元,跳过
swap(a[p],a[nl]);
//将主元所在的行换到上面
for(int i=1;i<=n;i++)
{
if(i==nl) continue;
double tmp=a[i][k]/a[nl][k];
for(int j=1;j<=n+1;j++)
a[i][j]-=tmp*a[nl][j];
//将该主元所在的列中的非零元素都消去,使得这一列中只有主元一个非零元素
}
nl++;
}
nl--;
if(nl<n)
{
//如果行数不够n,说明含有自由元
for(int i=nl+1;i<=n;i++)
{
//如果常数项不为0说明是无解的
if(a[i][n+1]!=0){cout<<"-1\n";return;}
}
//如果常数项是0说明是无穷解
cout<<"0\n";return;
}
for(int i=1;i<=n;i++)
{
//常数项/主元系数就是未知数的解
cout<<"x"<<i<<"="<<fixed<<setprecision(2)<<a[i][n+1]/a[i][i]<<endl;
}
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n+1;j++) cin>>a[i][j];
gauss();
system("pause");
return 0;
}
P2962 [USACO09NOV]Lights G - dfs+高斯消元
这题也是异或方程组,可以发现假设每个灯按的次数是xi,那么对于每一个灯来说
xj1^xj2^xj3^...^xi^...^xjm=1,j[1...m]是与i相连的灯,n个灯就构成了n元异或方程组,高斯消元后,如果有自由元的存在就要枚举这个灯开着优还是关掉优,最后取一个最优值即可
题解 P2962 【[USACO09NOV]灯Lights】 - Youngsc_AFO 的博客 - 洛谷博客 (luogu.com.cn)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-8;
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int sgn(double x)
{
if(fabs(x)<eps) return 0;
else if(x<0) return -1;
else return 1;
}
int getinv(int a){return qpow(a,mod-2LL);}
int a[55][55],n,m;
int vis[55],ans;
void gauss()
{
int nl=1;
for(int k=1;k<=n;k++)
{
int p=nl;
for(int i=nl+1;i<=n;i++) if(a[p][k]<a[i][k]) p=i;
if(a[p][k]==0) continue;
swap(a[p],a[nl]);
for(int i=1;i<=n;i++)
{
if(i==nl||a[i][k]==0) continue;
for(int j=1;j<=n+1;j++)
a[i][j]^=a[nl][j];
}
nl++;
}
nl--;
}
void dfs(int pos,int res)
{
if(ans<=res) return;
if(pos==0)
{
ans=res;return;
}
int ma=n+1;
for(int i=pos;i<=n;i++)
{
if(a[pos][i]){ma=i;break;}
}
if(ma<=n)
{
int num=a[pos][n+1];
for(int i=ma+1;i<=n;i++)
{
if(a[pos][i]) num^=vis[i];
}
dfs(pos-1,res+num);
}
else
{
vis[pos]=0;
dfs(pos-1,res);
vis[pos]=1;
dfs(pos-1,res+1);
vis[pos]=0;
}
}
void print()
{
cout<<"-----------------------------\n";
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n+1;j++)
cout<<a[i][j];
cout<<endl;
}
cout<<"------------------------------\n";
}
signed main()
{
//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//freopen("in.txt","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++) a[i][i]=a[i][n+1]=1;
for(int i=1;i<=m;i++)
{
int u,v;cin>>u>>v;
a[u][v]=a[v][u]=1;
}
//print();
gauss();
ans=inf;
//print();
dfs(n,0);
cout<<ans<<endl;
system("pause");
return 0;
}
P5027 Barracuda - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
根据题意列出方程然后枚举每一个方程是错误的,注意判断不合法的几个细节就可以,然后比较的时候一定要fabs,虽然都是正的,但是不加就是会wa,,,
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-10;
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int sgn(double x)
{
if(fabs(x)<eps) return 0;
else if(x<0) return -1;
else return 1;
}
bool integer(double s)
{
if(abs(round(s)-s)<eps) return 1;
return 0;
}
int getinv(int a){return qpow(a,mod-2LL);}
double a[105][105],b[105];
int n;
vector<int>v[105];
int gauss()
{
int nl=1;
for(int k=1;k<=n;k++)
{
int p=nl;
for(int i=nl+1;i<=n;i++) if(fabs(a[p][k])<fabs(a[i][k])) p=i;
//加上fabs才对,不知道为何,明明都是正的呀
if(sgn(a[p][k])==0) continue;
swap(a[p],a[nl]);
for(int i=1;i<=n;i++)
{
if(i==nl) continue;
double tmp=a[i][k]/a[nl][k];
for(int j=1;j<=n+1;j++)
a[i][j]-=tmp*a[nl][j];
}
nl++;
}
nl--;
if(nl<n) return 0;
double res=0;
int ma=n+1;
for(int i=1;i<=n;i++)
{
double tmp=a[i][n+1]/a[i][i];
if(tmp<0||sgn(a[i][n+1])==0) return 0;
int ztmp=tmp;
if(sgn(tmp-ztmp)!=0) return 0;
if(res<tmp)
{
res=tmp;ma=i;
}
}
int cnt=0;
for(int i=1;i<=n;i++)
{
double tmp=a[i][n+1]/a[i][i];
if(sgn(res-tmp)==0) cnt++;
}
if(cnt>1) return 0;
return ma;
}
void print()
{
cout<<"-----------------------------\n";
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n+1;j++)
cout<<a[i][j];
cout<<endl;
}
cout<<"------------------------------\n";
}
signed main()
{
//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//freopen("in.txt","r",stdin);
cin>>n;
for(int i=1;i<=n+1;i++)
{
int m;cin>>m;
for(int j=1;j<=m;j++)
{
int x;cin>>x;v[i].push_back(x);
}
cin>>b[i];
}
int ans=-1,mark=0;
for(int i=1;i<=n+1;i++)
{
for(int j=1;j<=n;j++)
for(int k=1;k<=n+1;k++) a[j][k]=0;
int cnt=0;
for(int j=1;j<=n+1;j++)
{
if(i==j)continue;
cnt++;
for(int k=0;k<v[j].size();k++)
{
a[cnt][v[j][k]]=1;
//cout<<v[j][k]<<" "<<v[j].size()<<" "<<j<<endl;
}
a[cnt][n+1]=b[j];
}
//print();
int flag=gauss();
//print();
if(flag)
{
mark++;
ans=flag;
if(mark>1){ans=-1;break;}
}
}
if(ans==-1) cout<<"illegal\n";
else cout<<ans<<endl;
system("pause");
return 0;
}
P2447 [SDOI2010] 外星千足虫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
一开始就构造成m行n+1列的方程组直接高斯消元,记录最大用到的行就是最小的K,N太大就用bitset优化,有一篇题解是不需要bitset的,但是我不知道他的做法为什么对就没采用,还是老老实实的用bitset吧
题解 P2447 【[SDOI2010]外星千足虫】 - YoungNeal 的博客 - 洛谷博客
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-10;
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int sgn(double x)
{
if(fabs(x)<eps) return 0;
else if(x<0) return -1;
else return 1;
}
bool integer(double s)
{
if(abs(round(s)-s)<eps) return 1;
return 0;
}
int getinv(int a){return qpow(a,mod-2LL);}
int n,m,b[2005],ans;
char s[2005][1005];
bitset<1005>a[2005];
bool gauss()
{
ans=0;
int nl=1;
for(int k=1;k<=n;k++)
{
int p=nl;
for(int i=nl;i<=m;i++) if(a[i][k]==1){p=i;break;}
if(a[p][k]==0) return 0;
ans=max(ans,p);
swap(a[p],a[nl]);
for(int i=1;i<=m;i++)
{
if(i==nl||a[i][k]==0) continue;
// for(int j=k;j<=n+1;j++)
// a[i][j]^=a[nl][j];
a[i]^=a[nl];
}
nl++;
}
nl--;
return nl>=n;
}
void print()
{
cout<<"-----------------------------\n";
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n+1;j++)
cout<<a[i][j];
cout<<endl;
}
cout<<"------------------------------\n";
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//freopen("in.txt","r",stdin);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>(s[i]+1);
for(int j=1;j<=n;j++) a[i][j]=(s[i][j]-'0');
int x;cin>>x;
a[i][n+1]=x;
}
if(gauss())
{
cout<<ans<<endl;
for(int i=1;i<=n;i++)
{
if(a[i][n+1]^a[i][i]) cout<<"Earth\n";
else cout<<"?y7M#\n";
}
}
else cout<<"Cannot Determine\n";
system("pause");
return 0;
}
P2973 [USACO10HOL]Driving Out the Piggies G - 高斯消元解方程组,概率
假设期望是经过这个点几次才会爆炸,那么期望其实是和概率在数值上是一样的了,也就是把每走一步的代价看作1,也就相当于在求期望了,f[i]表示经过了多少次i点会爆炸,然后根据题意就可以列出
f[i]=1+sum(f[j]*(1-P)*(1/in[j])) (i==1)
f[i]=sum(f[j]*(1-P)*(1/in[j])) (i!=1)
P是爆炸的概率,in[j]是j的度数,j可以到达i,j一共可以到达in[j]个点,j到达i的概率就是1/in[j],f[i]就可以通过j来转移,当i==1时因为一开始的起点就是1,所以1这个点一开始就经过了一次,所以要加上1
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-10;
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int sgn(double x)
{
if(fabs(x)<eps) return 0;
else if(x<0) return -1;
else return 1;
}
bool integer(double s)
{
if(abs(round(s)-s)<eps) return 1;
return 0;
}
int getinv(int a){return qpow(a,mod-2LL);}
int e[305][305],n,m,in[305];
double p,q,a[305][305],P;
void gauss()
{
int nl=1;
for(int k=1;k<=n;k++)
{
int p=nl;
for(int i=nl+1;i<=n;i++) if(a[p][k]<a[i][k]) p=i;
if(sgn(a[p][k])==0) continue;
swap(a[p],a[nl]);
for(int i=1;i<=n;i++)
{
if(i==nl) continue;
double tmp=a[i][k]/a[nl][k];
for(int j=1;j<=n+1;j++)
a[i][j]-=tmp*a[nl][j];
}
nl++;
}
for(int i=1;i<=n;i++)
{
double tmp=a[i][n+1]/a[i][i]*P;
cout<<tmp<<endl;
}
}
signed main()
{
//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//freopen("in.txt","r",stdin);
cin>>n>>m>>p>>q;
P=p/q;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
in[u]++;in[v]++;
e[u][v]=e[v][u]=1;
}
for(int i=1;i<=n;i++) a[i][i]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(e[i][j]) a[i][j]-=a[j][j]*(1.0-P)/in[j];
}
}
a[1][n+1]=1;
gauss();
system("pause");
return 0;
}