把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF895C】Square Subsets(线性基)

点此看题面

  • 给定\(n\)个元素,问有多少非空子集满足乘积为完全平方数。
  • \(n\le10^5,a_i\le70\)

完全平方数与线性基

一个数是完全平方数,等价于它的每个质因子的次数都是偶数。

考虑到\(a_i\le70\),质因子个数很少,因此我们可以直接把每个数质因数分解,由于一个数的贡献只需要考虑它的每种质因数次数的奇偶性,所以就可以被表示为一个二进制数。

这样一来,一个非空子集的乘积为完全平方数,当且仅当其中数对应的二进制数异或和为\(0\)

要判断有多少数异或和为\(0\)就很简单,只要把所有数插入线性基,那么线性基外的每个数都可以表示为线性基中若干数的异或和,进而线性基外的每个非空集合都可以表示为线性基中若干数的异或和。

所以假设线性基外有\(t\)个数,则答案就是\(2^t-1\)

代码:\(O(19n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define X 1000000007
using namespace std;
const int Pt=19,P[19]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};//预先存下所有质数
struct LB {int v[Pt];I bool A(RI x) {for(RI i=Pt-1;~i;--i) if(x>>i&1) {if(v[i]) x^=v[i];else return v[i]=x;}return 0;}}B;//线性基
int main()
{
	RI n,i,x,v,t=0;scanf("%d",&n);W(n--) {for(scanf("%d",&x),v=i=0;i^Pt;++i) W(!(x%P[i])) x/=P[i],v^=1<<i;!B.A(v)&&++t;}
	RI p=1;for(i=1;i<=t;++i) p=2LL*p%X;return printf("%d\n",p-1),0;//2^t-1
}
posted @ 2021-03-31 13:40  TheLostWeak  阅读(103)  评论(0编辑  收藏  举报