【Vijos】P1158 小三学算术

描述

小三的三分球总是很准的,但对于数学问题就完全没有想法了,他希望你来帮他解决下面的这个问题:对于给定的 n ,从 1! 2! 3! 、……、 n! 中至少删去几个数,才可以使剩下的数的乘积为完全平方数?

输入格式

仅一行,包含一个整数 n(1n500)

输出格式

第一行包含一个整数 k ,表示最少需要删去的数字个数。
接下来一行,从小到大排列的 k [1,n] 之间的整数,给出删数的方案。如果方案不止一种,输出方案从小到大排序序列最小的一组即可。

Solution

第一句话是喂给那些陷入高斯消元枚举自由元的人的:如果不想写 ida ,那么就要用一个结论:答案不超过 3

考虑对于一个数 k ,它是不是完全平方数,即它的质因子的个数是否都为偶数。那么直接把每个数表示为它的质因子以及每个质因子的个数,这样就可以方便地判断除法之后是否为完全平方数了。

进一步,我们可以直接把每个质因子个数表示为“奇偶”,因为我们并不需要质因子的个数,只需要其奇偶性。(压位用)

那么暴力枚举,计算过程我想应该不必多说了。(实测应该不压位也可以跑过)

#include
#define N 501

typedef long long ll;
int n,tot,pri[100];
bool b[N];
ll m,mask[N][2],aim0,aim1;

int main()
{
    scanf("%d",&n);
    for (int i=2;i<23;i++) if (!b[i]) for (int j=i<<1;j<501;j+=i) b[j]=1;
    for (int i=2;i<500;i++) if (!b[i]) pri[tot++]=i;
    for (int i=2,k;i<=n;i++)
    {
        k=i;m=1;
        for (int j=0;j<50 && pri[j]<=k;j++,m<<=1) while (k % pri[j]==0)
        {
            k/=pri[j];
            mask[i][0]^=m;
        }
        m=1;
        for (int j=50;j1)  while (k % pri[j]==0)
        {
            k/=pri[j];
            mask[i][1]^=m;
        }
        mask[i][0]^=mask[i-1][0];
        mask[i][1]^=mask[i-1][1];
    } 
    if (mask[n][0]==0 && mask[n][1]==0)
    {
        puts("0");
        return 0;
    }
    for (int i=2;i<=n;i++) aim0^=mask[i][0],aim1^=mask[i][1]; 
    for (int i=2;i<=n;i++) if (mask[i][0]==aim0 && mask[i][1]==aim1)
    {
        puts("1");
        printf("%d\n",i); 
        return 0;
    }
    for (int i=2;ifor (int j=i+1;j<=n;j++) if (aim0==(mask[i][0]^mask[j][0]) && aim1==(mask[i][1]^mask[j][1]))
    {
        puts("2");
        printf("%d %d\n",i,j); 
        return 0;
    }
    for (int i=2;i1;i++) for (int j=i+1;jfor (int k=j+1;k<=n;k++)
     if (aim0==(mask[i][0]^mask[j][0]^mask[k][0]) && aim1==(mask[i][1]^mask[j][1]^mask[k][1]))
    {
        puts("3");
        printf("%d %d %d\n",i,j,k);
        return 0;
    }
} 

你可能感兴趣的:(Vijos)