题意
给 (n) 组,每组 (4) 个、长度都为 (m) 且一样的字符串,
定义了字符串的相加为模 (5) 加法。
(q) 次询问,要求回答要构成每次询问要求的字符串有多少种方案。
思路
高斯消元,答案为 (5) 的自由元个数次方。
注意将所有询问都加入常数矩阵,可以在 (O(nm(n+q))) 的时间复杂度内完成,不然会超时。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int n,m,q;
LL a[510][810],ans,inv[5];
char s[510];
LL qpow(LL x,LL k){
LL res=1;
while(k){
if(k&1) res=res*x%mod;
x=x*x%mod;
k>>=1;
}
return res;
}
int main(){
for(int i=1;i<5;i++) inv[i]=i*i*i%5;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++) a[j][i]=s[j]-'a';
}
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++) a[j][i+n]=s[j]-'a';
}
for(int i=1,pos=1;pos<=n;i++){
//找当前主元,pos为假定的主元位
while(pos<=n){
for(int j=i+1;j<=m;j++) if(a[j][pos]>0) swap(a[j],a[i]);
//此时pos位上有不为0的系数,可以消元
if(a[i][pos]) break;
//否则找下一位有没有不为0的系数。
pos++;
}
//如果pos>n,则消元结束,且自由元数量 n-i+1
//如果方程组有解,那么答案就是 5^(n-i+1)
if(pos>n) {ans=qpow(5,n-i+1);break;}
//消元,不要求解出方程,就没有反代了
for(int j=i+1;j<=m;j++)
if(a[j][pos]!=0){
LL t=a[j][pos]*inv[a[i][pos]]%5;
for(int k=pos;k<=n+q;k++) a[j][k]=((a[j][k]-t*a[i][k])%5+5)%5;
}
}
//判断方程组是否有解
for(int i=n+1;i<=n+q;i++){
LL temp=ans;
for(int j=1,cnt=0;j<=m;j++,cnt=0){
for(int k=1;k<=n;k++) cnt+=a[j][k]>0;
//如果系数全 0,而常数非 0,则无解
if(!cnt&&a[j][i]) temp=0;
}
cout<<temp<<endl;
}
return 0;
}