您的位置:首页 > 其它

HDU 5833 Zhu and 772002 (数论+高斯消元)

2016-08-14 20:02 225 查看
题目链接

题意:给定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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: