UVa11542

来源:互联网 发布:电竞椅 知乎 编辑:程序博客网 时间:2024/06/04 17:55

题目链接

分析:
题目特意说到,不含有超过500的素因子
这就是在提示我们可以质因数分解
我们把这n个数分解成若干素数乘积的形式,并用一个向量表示

如:4 6 10 15
这n=4个数中,涉及到的素数只有2,3,5这三个
分解后:
4=2^2*3^0*5^0 ——>{2,0,0}
6=2^1*3^1*5^0 ——>{1,1,0}
10=2^1*3^0*5^1 ——>{1,0,1}
15=2^0*3^1*5^1 ——>{0,1,1}

我们现在按照题目要求,选出若干数乘起来,
得到的数字(我们称为W)也一定可以用2,3,5这三个质数表示

为了辅助该数的表示,我们设一个01向量x
xi表示第i位数字是否要选(选是1,不选是0)
这样我们按照题目求出的数就可以如下表示了:
这里写图片描述
因为只有第一个数(4),第二个数(6),第三个数(10)有2这个因子,
所以得到的W中2的指数只能由这三个数决定,其余的同理

如果W可以被开方,显然每个质数的次数都要是偶数,即
x2+x3=0 (mod 2)
x2+x4=0 (mod 2)
x3+x4=0 (mod 2)
由于异或和数字的奇偶性有很大关系,所以上述的方程组可以变成异或方程
x2 XOR x3=0
x2 XOR x4=0
x3 XOR x4=0

这样我们就可用高斯消元优美的解决了

最后的答案是

2^(自由元的个数)-1

因为每个自由元都有两种选择,但是题目不允许我们一个都不选,所以要-1

tip

在阵式的构造上,
a[i][j]=1表示第j个数字有第i个质数这个因子,且该因子在第j个数中的次数是奇数
换句换说就是,第i个质数的次数会受到第j个数的影响

gauss消元没有固定的模板,重要的是掌握ta的思维方式

//这里写代码片#include<cstdio>#include<cstring>#include<iostream>#define ll long longusing namespace std;int a[102][102];     //500以内的素数只有95个  int sshu[102],tot=0,maxp,n; bool no[502];void prime(int n){    for (int i=2;i<=n;i++)    {        if (!no[i]) sshu[++tot]=i;        for (int j=1;j<=tot&&sshu[j]*i<=n;j++)        {            no[sshu[j]*i]=1;            if (i%sshu[j]==0) break;        }    }}int gauss(int n,int m)                     //n个未知量,m个方程 {    int cnt=0;    int now=1;    for (int i=1;i<=n;i++)                 //枚举未知量     {        int num=now;                       //消元消到了第几个方程         for (int j=now;j<=m;j++)             if (a[j][i])            {                num=j;                break;            }        if (!a[num][i]) continue;        if (now!=num)            for (int j=1;j<=n+1;j++)                swap(a[now][j],a[num][j]);        for (int j=1;j<=m;j++)             if (a[j][i]&&j!=now)                for (int k=1;k<=n+1;k++)                    a[j][k]^=a[now][k];        now++;    }    return n-now+1;}int main(){    prime(500);    int T;    scanf("%d",&T);    while (T--)    {        memset(a,0,sizeof(a));        scanf("%d",&n);        ll x;                                  //注意x的范围         maxp=0;                                //n个数中涉及到的最大素数         for (int i=1;i<=n;i++)        {            scanf("%lld",&x);            for (int j=1;j<=tot&&x!=1;j++)                while (x%sshu[j]==0)                {                    a[j][i]^=1;                //sshu[j]的系数可能有第i个数的影响                     x/=sshu[j];                    maxp=max(maxp,j);                }        }        int ans=gauss(n,maxp);        printf("%lld\n",(1LL<<ans)-1);     }    return 0;}
原创粉丝点击