题目传送门
题意:
个数,第 个数是 。
从这些数中选出若干个数,这些数的乘积需要是平方数。问有多少种方案。
不能选0个数,且不同方案需要两个方案中至少有一个数的下标不同。
数据范围: , 。
题解:
不超过 的质数有 个,考虑状压。
位二进制数 ,每位的 代表当前位对应质数出现偶数次,每位的 代表当前位对应质数出现奇数次。
表示数值大小范围 中选择若干个数的质因子状压为 的方案数。
不超过 , 是 级别。不会超时,但是会MLE,滚动数组即可。
感受:
最近不在状态,也可能是本身弱,写题很不顺。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 5 ;
const ll mod = 1e9 + 7 ;
int n , a[maxn] , num[80] ;
int cnt = 0 , p[30] ;
ll two[maxn] ;
ll dp[2][maxn] ;
int su(int x)
{
for(int i = 2 ; i * i <= x ; i ++)
if(x % i == 0) return 0 ;
return 1 ;
}
int ji(int x)
{
int y = 1 ;
int ans = 0 ;
for(int i = 1 ; i <= 19 ; i ++)
{
int num = 0 ;
while(x % p[i] == 0)
{
num ^= 1 ;
x /= p[i] ;
}
if(num) ans += y ;
y <<= 1 ;
}
return ans ;
}
int main()
{
scanf("%d" , &n) ;
memset(num , 0 , sizeof(num)) ;
memset(dp , 0 , sizeof(dp)) ;
for(int i = 1 ; i <= n ; i ++)
{
int x ;
scanf("%d" , &x) , num[x] ++ ;
}
for(int i = 2 ; i <= 70 ; i ++)
if(su(i)) p[++ cnt] = i ;
two[0] = 1ll ;
for(int i = 1 ; i <= 1e5 ; i ++) two[i] = two[i - 1] * 2ll % mod ;
dp[1][0] = 1ll * two[num[1]] % mod ;
for(int i = 2 ; i <= 70 ; i ++)
{
int x = ji(i) ;
int y = 0 ;
int z = max(0 , num[i] - 1) ;
for(int j = 0 ; j < (1 << 19) ; j ++)
{
if(num[i] >= 1)
dp[i % 2][x ^ j] += two[z] * dp[1 - (i % 2)][j] % mod ;
dp[i % 2][y ^ j] += two[z] * dp[1 - (i % 2)][j] % mod ;
dp[i % 2][x ^ j] %= mod ;
dp[i % 2][y ^ j] %= mod ;
}
memset(dp[1 - (i % 2)] , 0 , sizeof(dp[1 - (i % 2)])) ;
}
printf("%lld\n" , (dp[0][0] - 1ll + mod) % mod) ;
return 0 ;
}