• 题解[CF895C Square Subsets]


    题目描述

    给定数组(A) ,选取(A)的非空子集是他们的乘积为完全平方数。

    (1leq a_ileq 70)

    Sol

    70以内的质数只有19个,这提示我们可以状压。

    (f[i][S])为考虑了前(i)种数值,且质因子的状态为(S)

    状态(S):若第(j)个质因子被使用的次数是奇数次,那(S)在二进制下的第(j)位是一。

    转移时对数值进行质因数分解,设质因数分解后得到状态(T),这个数值的数量为(cnt)

    考虑转移:

    若使用奇数个此数值:

    [f[i][S|T]=f[i-1][S]cdot2^{cnt-1} ]

    若使用偶数个此数值:

    [f[i][S]=f[i-1][S]cdot2^{cnt-1} ]

    为什么是乘上(2^{cnt-1})呢?

    因为选取此值共有(2^{cnt})种方案,其中选奇数个和选偶数个的情况各占一半。

    Code

    #include<bits/stdc++.h>
    #define N (100010)
    #define ll long long
    using namespace std;
    const int p[21]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
    const ll P=1000000007;
    int n,cnt[71];
    ll pw[N],f[2][1000000];
    inline int read(){
    	int w=0;
    	char ch=getchar();
    	while(ch>'9'||ch<'0') ch=getchar();
    	while(ch>='0'&&ch<='9'){
    		w=(w<<3)+(w<<1)+(ch^48);
    		ch=getchar();
    	}
    	return w;
    }
    int main(){
    	n=read(),pw[0]=1,f[0][0]=1;
    	for(int i=1;i<=n;i++) ++cnt[read()],pw[i]=pw[i-1]*2%P;
    	int now=0;
    	for(int i=1;i<=70;i++){
    		if(!cnt[i]) continue;
    		now^=1;
    		memset(f[now],0,sizeof(f[now]));
    		for(int S=0;S<(1<<19);S++){
    			int T=S,num=i;
    			for(int j=1;j<=19&&num>=p[j];j++)
    				while(num%p[j]==0) num/=p[j],T^=(1<<(j-1));
    			f[now][T]=(f[now][S]+1ll*pw[cnt[i]-1]*f[now^1][S]%P)%P;
    			f[now][S]=(f[now][S]+1ll*pw[cnt[i]-1]*f[now^1][S]%P)%P;
    		}
    	}
    	printf("%d
    ",f[now][0]-1);
    	return 0;
    }
    

    完结撒花❀

  • 相关阅读:
    题解 DTOJ #1438. 矮人排队(lineup)
    题解 DTOJ #4423. 「THUSC2019」塔
    题解 DTOJ #4123.「2019冬令营提高组」全连
    题解 DTOJ #4016.辉夜的夜空明珠(moon)
    题解 DTOJ #2498.大步小步(babystep)
    题解 DTOJ #3326.组队(group)
    题解 DTOJ #1515.三塔合一
    题解 DTOJ #2305.Bazarek
    【code】Splay 模板
    寻找乱序数组中第K大的数
  • 原文地址:https://www.cnblogs.com/xxbbkk/p/15220895.html
Copyright © 2020-2023  润新知