zoukankan      html  css  js  c++  java
  • Agc003_D AntiCube

    传送门

    题目大意

    给定$N$个数,求一个最大的子集,使得任意两两的乘积不是一个完全立方数。

    $nleq 10^5 A_ileq 10^{10}$

    题解

    考虑两两乘积为$x^3$,由于$x^3leq 10^{20}$,那么$xleq 10^{frac{20}{3}}$,那么$x$最多出现一个超过$10^{frac{10}{3}}$的质因子,且这一质因子不会超过$10^{frac{20}{3}}$。又由于$A_ileq 10^{10}$,所以至多出现一个$> 10^5$的质因子,由于$x^3$由两个$A_i$相乘,那么$x$一定不会出现超过$>10^5$的质因子。

    所以$x$由$[2,10^5]$内的质数幂构成,且$[10^{frac{10}{3}},10^5]$内质数至多出现$1$个,且指数不超过$1$。

    那么考虑筛出$10^5$以内的质数。

    对于每一个$A_i$我们直接暴力枚举$<10^{frac{10}{3}}$的质数(大约$300$个左右),直接筛掉,过滤掉质数是$3$的倍数的质因子,记录它由哪几个剩下的质因子构成,并将它每一个质因子质数模$3$的余数($1$或$2$)压进一个二进制状态中。对于剩下的数,如果它不为$1$,我们判断它是不是一个$<10^5$的质数或者一个$<10^5$的质数的平方,如果不是,那么它一定无法和别的任何一个数组成立方数,因为它肯定是一个$>10^5$的质数。否则,我们直接把这个稍大的质数当做之前过滤的数一样压入状态中。

    对于两个过滤完的质因子的集合完全相同的数,它们能组成立方数当且仅当它们的二进制状态是恰好相反的,这样就能凑出每一个质因子的质数都是$3$的倍数。

    然后就可以直接按照质因子集合放在一起,记录每一种状态和它的补集包含的数的数量,选集合大小更大的那一堆即可。

    复杂度$O(nlog n+300n)$。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 300020
    #define MAXN 100000
    using namespace std;
    int n,m,sz[M],K[M],res,mp[200002];
    int od[M],ans; LL p[M],pri[M],t[M]; bool isp[M];
    bool cmp(int x,int y){return t[x]<t[y];}
    LL read(){
    	LL nm=0; char cw=getchar(); for(;!isdigit(cw);cw=getchar());
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0'); return nm;
    }
    int main(){
    	memset(isp,true,sizeof(isp));
    	for(int i=2;i<MAXN;i++){
    		if(!isp[i]) continue; pri[++m]=i;
    		for(int j=(i<<1);j<MAXN;j+=i) isp[j]=false;
    	}
    	n=read();
    	for(int j,i=1;i<=n;i++){
    		for(p[i]=read(),K[i]=sz[i]=0,j=t[i]=1;j<=308;j++){
    			if(p[i]%pri[j]) continue; int cnt=0;
    			while(!(p[i]%pri[j])) p[i]/=pri[j],++cnt; cnt%=3;
    			if(cnt) K[i]|=((cnt&1)<<sz[i]),sz[i]++,t[i]*=pri[j];
    		}
    		if(p[i]>1ll){
    			if(p[i]<MAXN) K[i]|=(1<<sz[i]),sz[i]++,t[i]*=p[i];
    			else {
    				LL qq=sqrt(p[i]);
    				if(qq*qq!=p[i]) ans++,i--,n--;
    				else sz[i]++,t[i]*=qq;
    			}
    		}
    	}
    	for(int i=1;i<=n;i++) od[i]=i; sort(od+1,od+n+1,cmp);
    	for(int l=1,r;l<=n;l=r+1){
    		for(r=l;r<n&&t[od[r]]==t[od[r+1]];r++);
    		if(t[od[l]]==1){ans++;continue;}
    		int maxn=(1<<sz[od[l]])-1;
    		for(int i=l;i<=r;i++) mp[K[od[i]]]++;
    		for(int i=l;i<=r;i++){
    			int x=K[od[i]],ot=(maxn^K[od[i]]);
    			if(mp[x]>=mp[ot]) ans+=mp[x],mp[x]=mp[ot]=0;
    		}
    	} printf("%d
    ",ans); return 0;
    }
  • 相关阅读:
    [CQOI2015]选数
    [AHOI2009]中国象棋
    [ZJOI2012]灾难
    [NOI2018]屠龙勇士
    [APIO2016]划艇
    [ZJOI2011]礼物
    cent 7 识别exfat
    C语言风格的 for 循环(SHELL的循环写法 已验证20200517)
    系统安装时间
    单用户模式修改root密码
  • 原文地址:https://www.cnblogs.com/OYJason/p/9837304.html
Copyright © 2011-2022 走看看