zoukankan      html  css  js  c++  java
  • UVa11542Squre——异或方程组&&高斯消元法

    题意

    给出 $n$ 个整数,从中选出1个或多个,使得选出的整数乘积是完全平方数。一共有多少种选法?($1 leq n leq 100$,$1 leq a_i leq 10^{15}$ 且不含大于500的素因子)

    分析

    “不含大于500的素因子”提示我们考虑每个数的素因数分解,用01向量表示一个数,再用 $n$ 个01变量 $x_i$ 来表示我们的选择,其中 $x_i=1$ 表示要选第 $i$ 个数,$x_i=0$ 表示不选第 $i$ 个数,则对每个素数的幂列出一个模2的方程。

    如果要让这个数是完全平方数,每个幂都应该是偶数,即模2为0.

    在模2的剩余系中每个数都是0/1,加法可换成异或,于是就成了一个异或方程组。

    xor方程组很好消元的,因为不需要做乘法和除法,只需要做xor;每次也不需要找绝对值最大的系数(因为系数不是0就是1,只需系数为1即可消元)

    设自由变量为 $r$,则最终答案为 $2^r-1$,减1是因为本题不允许一个整数都不选。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int maxn = 500 + 10;
    
    typedef int Matrix[maxn][maxn];
    
    //m个方程,n个变量
    int Rank(Matrix A, int m, int n)    //求矩阵的秩
    {
        int i = 0, j = 0, k, r, u;
        while(i < m && j < n)      //当前正在处理第i个方程的第j个变量
        {
            r = i;
            for(k = i;k < m;k++)
                if(A[k][j]){r =k; break;}
            if(A[r][j])
            {
                if(r != i)  for(k = 0; k <= n;k++)  swap(A[r][k], A[i][k]);
                for(u = i+1; u < m;u++)  if(A[u][j])
                    for(k = i;k <= n;k++)  A[u][k] ^= A[i][k];
                i++;
            }
            j++;
        }
        return i;
    }
    
    //返回n以内素数的个数
    //埃氏筛法O(nloglogn)
    const int maxs = 500 + 10;
    int prime[maxs];            //prime[i]表示第i个素数
    bool is_prime[maxs + 1];    //is_prime[i]为true表示i是素数
    
    int sieve(int n)
    {
        int cnt = 0;
        for (int i = 0; i <= n; i++)  is_prime[i] = true;
        is_prime[0] = is_prime[1] = false;
        for (int i = 2; i <= n; i++)
        {
            if (is_prime[i])
            {
                prime[cnt++] = i;
                for (int j = i * i; j <= n; j += i)  is_prime[j] = false;  //i * i可能爆int
            }
        }
        return cnt;
    }
    
    Matrix A;
    
    int main()
    {
        int m = sieve(500);
    
        int T;
        scanf("%d", &T);
        while(T--)
        {
            int n, max_p=0;
            ll x;
            scanf("%d", &n);
            memset(A, 0, sizeof(A));       //记得置零
            for(int i = 0;i < n;i++)
            {
                scanf("%lld", &x);
                for(int j = 0;j < m;j++)
                {
                    while(x % prime[j] == 0)
                    {
                        max_p = max(max_p, j);
                        x /= prime[j];
                        A[j][i] ^= 1;
                    }
                }
            }
            int r = Rank(A, max_p+1, n);
            printf("%lld
    ", (1LL << (n-r))-1);
        }
        return 0;
    }

    From:

    《算法竞赛入门经典训练指南》——刘汝佳、陈锋著

  • 相关阅读:
    02 Filter过滤器
    Vue入门
    Git教程
    Markdown 常用语言关键字
    王爽《汇编语言》(第三版)实验16解析
    王爽《汇编语言》(第三版)实验15解析
    王爽《汇编语言》(第三版)实验14解析
    王爽《汇编语言》(第三版)实验13解析
    王爽《汇编语言》(第三版)实验12解析
    王爽《汇编语言》(第三版)实验11解析
  • 原文地址:https://www.cnblogs.com/lfri/p/11531672.html
Copyright © 2011-2022 走看看