题解:Square Subsets

题目传送门

题意分析

发现一个神奇的东西:\(a_i\leq70\)。值域如此之小,一定可以利用。因此考虑与值域有关的做法。

寻找完全平方数,则其所有质因子的指数均为偶数

可以发现,\([1,70]\) 中的质数有且仅有 \(19\) 个,可以考虑从此方面入手。

考虑影响是否为完全平方数的仅仅是质因子的指数的奇偶性,因此可以考虑状压 DP。

记第 \(i\) 个质数为 \(p_i,p_1=2\),设 \(\textit{dp}_{i,S}\) 表示选择完了 \(a\) 中的 \(1\sim i\),乘积的 \(p_j\) 因子质数的奇偶性为 \(S_j\) 时的方案数。则答案即 \(\textit{dp}_{n,\langle0,0,\cdots,0\rangle}\)。注意到 \(\vert S\vert\leq19\),显然可以压缩成一个二进制整数。

考虑如何递推。

\(\textit{cnt}_i\)\(i\)\(a\) 中的出现次数,先考虑 \(\textit{cnt}_i=1\) 的情况。

  • 若不选择 \(i\),那么方案数为 \(\textit{dp}_{i-1,S}\)
  • 否则,方案数为 \(\textit{dp}_{i-1,S\oplus s}\)。其中,\(s\) 表示 \(i\) 的状态。即对 \(i\) 质因数分解,同时存储 \(i\) 的质因子指数奇偶性。

则有:

\[\textit{dp}_{i,S}=\textit{dp}_{i-1,S}+\textit{dp}_{i-1,S\oplus s} \]

考虑 \(\textit{cnt}_i\neq1\) 的情况。

其实是一样的。\(\textit{cnt}_i\)\(i\),选择的总方案数为 \(2^{\textit{cnt}_i-1}\)

则有:

\[\textit{dp}_{i,S}=2^{\textit{cnt}_i-1}\left(\textit{dp}_{i-1,S}+\textit{dp}_{i-1,S\oplus s}\right) \]

注意到此时选择多少个 \(i\) 对于奇偶性造成的影响并不需要考虑,因为那样仅仅是交换了一下 \(\textit{dp}_{i-1,S},\textit{dp}_{i-1,S\oplus s}\) 而已。


注意到这样会 MLE,因此使用滚动数组优化掉前一维即可。

AC 代码

//#include<bits/stdc++.h>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
constexpr const int N=1e5,Vprime=19,V=70,P=1e9+7;
constexpr const int prime[Vprime]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
int n,a[N+1],dp[2][1<<Vprime|1],cnt[V+1];
int qpow(int base,int n){
	int ans=1;
	while(n){
		if(n&1){
			ans=(ll)ans*base%P;
		}
		base=(ll)base*base%P;
		n>>=1;
	}
	return ans;
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		cnt[a[i]]++;
	}
	bool mode;
	dp[mode][0]=1;
	for(int i=1;i<=V;i++){
		if(!cnt[i]){
			continue;
		} 
		mode=!mode;
		int s=0;
		for(int x=i,j=0;j<Vprime;j++){
			while(x%prime[j]==0){
				s^=1<<j;
				x/=prime[j];
			}
		}
		int pl=qpow(2,cnt[i]-1);
		for(int j=0;j<(1<<Vprime);j++){
			dp[mode][j]=(ll)pl*(dp[!mode][j]+dp[!mode][j^s])%P;
		}
	}
	int ans=dp[mode][0]-1;
	if(ans<0){
		ans+=P; 
	}
	cout<<ans<<'\n';
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-07-22 19:55  TH911  阅读(21)  评论(0)    收藏  举报