HDU 5833 Zhu and 772002 (数论+高斯消元)
题意:给定n个数,这n个数的素因子值不超过2000,从中取任意个数使其乘积为完全平方数,问有多少种取法。
题解:开始用素筛枚举写了半天TLE了,后来队友说高斯消元才想起来,果断用模板。赛后又得知这是个原题sgu200,真坑啊。把每个数进行素因子分解,素因子a的幂为奇数则视为1,偶数则视为0,转化为从n个数中取数异或和为0有多少种取法的问题。
AC代码:
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #define maxn 305 using namespace std; typedef long long ll; ll T, N; ll beg[maxn], end[maxn], x[maxn]; ll a[maxn][maxn]; ll Gauss_XOR(ll a[maxn][maxn], ll x[maxn], ll var, ll equ) { ll row, col; for (row = col = 1; row <= equ && col <= var; ++row, ++col) { if (!a[row][col]) { for (int i = equ; i > row; --i) { if (a[i][col]) { for (int j = row; j <= var + 1; ++j) { swap(a[i][j], a[row][j]); } break; } } } if (!a[row][col]) { --row; continue; } for (int i = row + 1; i <= equ; ++i) { if (a[i][col]) { for (int j = var + 1; j >= col; --j) { a[i][j] ^= a[row][j]; } } } } for (int i = row; i <= equ; ++i) { if (a[i][var + 1]) return -1; } if (row <= var) { return var - row + 1; } for (int i = var; i >= 1; --i) { x[i] = a[i][var + 1]; for (int j = i + 1; j <= var; ++j) { x[i] ^= a[i][j] && x[j]; } } return 0; } const long long mod=1000000007; ll prime[2050],cnt=0; ll isprime[2050]; ll data[50][10]; void get() { for(int i=0; i<=2000; i++) isprime[i]=1; for(int i=2; i<=2000; i++) { if(isprime[i]==1) { prime[cnt++]=i; for(int j=i+i; j<=2000; j+=i) isprime[j]=0; } } } int main() { ll num; int cas=1; get(); scanf("%lld", &T); while (T--) { ll equ = 0; memset(x, 0, sizeof (x)); memset(a, 0, sizeof (a)); scanf("%lld", &N); for(int i = 1; i <= N; ++i) { memset(data,0,sizeof(data)); ll pos = 1; scanf("%lld", &num); ll tmp1=num,tmp2=1; for(int j=0; j<cnt; j++) { int sum=0; if(tmp1%prime[j]==0) { tmp1/=prime[j]; sum++; while(tmp1%prime[j]==0) { tmp1/=prime[j]; sum++; } } if(sum%2==1) data[j/8][j%8]=1; } int b=305; int ii=0,jj=0; while(b--) { if(jj==8) { ii++; jj=0; } if(data[ii][jj++] & 1) a[pos][i] = 1; else a[pos][i] = 0; //num >>= 1; ++pos; } equ = max(equ, pos - 1); } for(int i = 1; i <= 32; ++i) a[i][N + 1] = 0; ll ans = Gauss_XOR(a, x, N, equ); if (ans == -1) puts("-1"); else { ll prt = 1; for(int i = 1; i <= ans; ++i) { prt <<= 1; prt %= mod; } printf("Case #%d:\n",cas++); prt=(prt-1+mod)%mod; printf("%lld\n", prt); } } return 0; }
之前写的TLE素筛:
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <queue> using namespace std; typedef long long ll; const long long mod=1000000007; ll prime[2050],cnt=0; ll isprime[2050],isprime2[2050]; ll data[300][2050]; void get() { for(int i=0; i<=2000; i++) isprime[i]=1; for(int i=2; i<=2000; i++) { if(isprime[i]==1) { prime[cnt++]=i; for(int j=i+i; j<=2000; j+=i) isprime[j]=0; } } } int main() { get(); //303 int num[cnt+5]; int t,cas=1,a[305]; /* ll c=1; for(int i=0;i<15;i++) c*=prime[i]; printf("%lld\n",b);*/ scanf("%d",&t); while(t--) { int n; memset(a,0,sizeof(a)); memset(data,0,sizeof(data)); scanf("%d",&n); for(int i=0; i<n; i++) scanf("%d",&a[i]); sort(a,a+n); ll sum=0,k=-1,flag1=0; ll tmp1; for(int i=0; i<2; i++) { sum=0; k=-1; flag1=0; for(int j=0; j<n; j++) { tmp1=a[j]; if(tmp1==-1) continue; if(tmp1%prime[i]==0) { //cout<<tmp1<<" *** "<<prime[i]<<endl; tmp1/=prime[i]; sum++; flag1++; while(tmp1%prime[i]==0) { tmp1/=prime[i]; sum++; } k=j; } if(flag1>=2) break; } //cout<<prime[i]<<" "<<sum<<" "<<flag1<<endl; if(flag1==1&&((sum%2)==1)) a[k]=-1; } for(int i=0; i<n; i++) { if(a[i]==-1) { for(int j=i; j<n; j++) { a[j]=a[j+1]; } i--; n--; } } ll tmp=1; memset(num,0,sizeof(num)); ll tmp2; for(int i=0;i<n;i++) { tmp=1; for(int j=0;j<cnt;j++) { tmp2=a[i]; if(!tmp2) break; if(tmp2%prime[j]==0&&tmp2!=0) { tmp2/=prime[j]; data[i][prime[j]]++; while(tmp2%prime[j]==0) { tmp2/=prime[j]; data[i][prime[j]]++; } data[i][prime[j]]%=2; } } } // cout<<data[0][3]<<endl; // cout<<data[1][3]<<endl; // cout<<data[2][2]<<endl; // for(int i=0;i<n;i++) // { // cout<<a[i]<<" "; // }cout<<endl; ll pre[2050]; ll ans=-1; for(int i=0;i<(1<<n);i++) { memset(pre,0,sizeof(pre)); for(int j=0;j<n;j++) { if(i&(1<<j)) { for(int x=2;x<=2000;x++) { pre[x]+=data[j][x]; pre[x]%=2; } } } int flag=0; for(int k=0;k<cnt;k++) { if(pre[prime[k]]%2==1) { flag=1; break; } } if(flag==0) { //cout<<i<<"***"<<endl; ans++; ans%=mod; } } printf("Case #%d:\n",cas++); printf("%lld\n",ans); } return 0; }