推荐两个博客: https://www.jianshu.com/p/888f2c2b31bc https://blog.csdn.net/weixin_43871207/article/details/108395566 我基本上就是看了上面两个博客,将代码的解析完善的,第一个博客理论讲的好,第二个博客代码写的好,每一句话都是精髓,如果不懂的话,一定反复阅读。 #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <iostream> #include <algorithm> #include <vector> #include <stack> #include <sstream> #include <map> #include <set> #include <queue> #include <stdlib.h> typedef long long ll; using namespace std; typedef long long ll; const ll inf = 0x3f3f3f3f3f3f3f3f; const ll mod = 1e9 + 7; const ll N = 2005; //要找出2000以内的素数(一共303个) ll a[2005][2005];//增广矩阵 ll x[2005]; //如果有唯一解,则计算解集,存到x数组中 ll freeX[N]; //标记是否为自由变元 ll prime[2005]; //用prime数组储存素数 bool vis[2005]; //vis数组,如果=1,则不是素数,如果!=1,则是素数,相当于一个标记函数 ll tot; //tot为素数的个数 void init() //初始化 -》 将素数找出来,存入prime数组里面 { memset(vis, 0, sizeof(vis)); vis[1] = vis[0] = 1; //1不是素数,0是素数 tot = 0; for(ll i = 2; i < N; ++i) { if(vis[i]==0) { prime[tot++] = i; for(ll j = 2*i; j < N; j += i) { vis[j] = 1; } } } } void solve(ll id, ll n) //id是第几位数(从0开始),n是第几位数的数值,解决第几个数前面是1还是0的问题 { for(ll i = 0; i < tot; ++i) //tot为素数的个数 { while(n % prime[i] == 0) //如果n是某一个素数的倍数 { n /= prime[i]; a[i][id] ^= 1; //a[i][j] i为第几个素数,j为第几个样例 ,a[i][j]为第j个数含有多少个第i个素数,如果含有偶数个素数,则结果为0,否则为1 //所以求解异或方程组自由变元的个数cnt,每个自由变元可取的值为0或1,所以答案为这些自由变元的组合,即2^cnt-1 } } } ll Gauss(ll equ,ll var) //返回自由变元个数,equ为行数(素数的个数),var为列数(输入元素的个数,也就是n) { //初始化:将解都设为0,标记都不是自由变元 for(ll i=0;i<=var;i++) { x[i]=0; freeX[i]=0; } /*转换为阶梯阵*/ ll col=0; //当前处理的列 ll row; //当前处理的行 ll num=0; //自由变元的序号 for(row=0; row<equ && col<var ; row++,col++) //枚举当前处理的行 { ll maxRow = row; //当前列绝对值最大的行 for(ll i=row+1;i<equ;i++) //寻找当前列绝对值最大的行 { if(abs(a[i][col])>abs(a[maxRow][col])) maxRow=i; } if(maxRow!=row) //与第row行交换 { for(ll j=row;j<var+1;j++) { swap(a[row][j],a[maxRow][j]); } } if(a[row][col]==0) //即当前这一列全是0,处理当前行的下一列 { freeX[num++]=col;//记录自由变元 row--;//在for循环里面row要++,因为处理当前行的下一列,行数不用变,只要变列数就可以了,所以row要--,抵消掉for循环里面的++ continue; } for(ll i=row+1;i<equ;i++) //从该行的下一行一直到最后一行 { if(a[i][col]!=0) { for(ll j=col;j<var;j++) //对于下面出现该列中有1的行,需要把1消掉 { a[i][j]^=a[row][j]; } } } } /*求解*/ //无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0 //因为n<303,即列数<行数,所以最后row的位置肯定是最后一列,但是不到最后一行,根据上面的for循环,从row行开始,一直到最后一行,前col-1列每个都是0,但是最后一列(col列)不一定都是0,如果有不是0的(就是1),那么无解。 for(ll i=row;i<equ;i++) //从第row行开始,一直到最后一行,如果最后一列的元素!=0,则无解,直接返回-1 { if(a[i][col]!=0) { return -1; } } //无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行(即n*(n+1)中出现某一行全是0这样的行) ll temp=var-row; //因为如果有自由变元,则只有列变化,行数是不变化的,所以最后的自由变元有n-row个 if(row<var) //返回自由变元数 { return temp; } //唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵 for(ll i=var-1;i>=0;i--) //计算解集,这道题可以不用计算解集,可以不用写 { x[i]=a[i][var]; for(ll j=i+1;j<var;j++) { x[i]^=(a[i][j]&&x[j]); } } //如果形成唯一解,最后返回的结果就是0,2^0-1=0 return 0; } ll enumFreeX(ll freeNum,ll var) //枚举自由元,统计有解情况下1最少的个数 ,这道题这里也可以不用写 { ll sta=(1<<(freeNum));//自由元的状态总数 ll res=inf; for(ll i=0;i<sta;++i){//枚举状态 ll cnt=0; for(ll j=0;j<freeNum;j++){//枚举自由元 if(i&(1<<j)){ cnt++; x[freeX[j]]=1; }else x[freeX[j]]=0; } for(ll k=var-freeNum-1;k>=0;k--){//没有自由元的最下面一行 ll index=0; for(index=k;k<var;index++){//在当前行找到第一个非0自由元 if(a[k][index]) break; } x[index]=a[k][var]; for(ll j=index+1;j<var;++j){//向后依次计算出结果 if(a[k][j]) x[index]^=x[j]; } cnt+=x[index];//若结果为1,则进行统计 } res=min(res,cnt); } return res; } ll qpow(ll a, ll b) //快速幂 { ll ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans % mod; } int main() { init(); //初始化,将素数放入prime数组里面,从prime[0]开始存 ll t, n; //给的数不超过300(n<=300) ll tmp; cin >> t; //一共几组样例 for (int I=1;I<=t;I++) { cin >> n; //给出几个数 memset(a, 0, sizeof(a)); //a是增广矩阵 for(ll i = 0; i < n; ++i) { cin >> tmp; solve(i, tmp); } ll freeNum = Gauss(tot,n); //用高斯消元求解异或方程组(解出自由元个数) (tot为素数的个数,n为一共输入多少个数) printf("Case #%d:\n%lld\n", I , (qpow(2, freeNum) - 1 + mod) % mod); //最后的结果为2^cnt(自由变元个数)-1 } return 0; }