CF1325E-数学,无向图最小环

CF1325E

题目描述

题目描述

题解

大好题啊,转换为图论做,太妙了
首先对每个数进行质因数分解,可以线性筛完成,我们将其中偶数个的素数因子都丢掉,由于题目限制,每个数的因数个数不超过 7 7 7个,所以最后每个数剩下的质因数只有 1 1 1 2 2 2个。然后最关键也是最妙的一步就是:
1.如果剩下的质因数个数为 2 2 2,我们就将这 2 2 2个质因数连接起来
2,如果剩下的质因数个数为 1 1 1,我们就将 1 1 1和这个质因数连接起来
那么这条边表示什么意思呢,表示如果我们走了这条边,那么我们就选择了这个数,于是每条边都代表一个数,因此问题转换了求无向图的最小环, W h y ? Why? Why?,我们考虑一个环,环中的每个点的度数都为 2 2 2,因此每个质因数都成了偶数个,那么最后一定满足乘积为完全平方,又因为求最少选出多少个,所以是求最小环.
那么求最小环又成了一个难题,如果枚举每个点来作为起点,然后 B F S BFS BFS,那么肯定 T T T飞,我们考虑优化,由于 1 ≤ a i ≤ 1 e 6 1\le a_i\le 1e6 1ai1e6,于是每个数的边不可能连接两个大于 1000 1000 1000的质数,因此边的一个端点一定是 [ 1 , 1000 ] [1,1000] [1,1000],于是环中的点肯定包含 [ 1 , 1000 ] [1,1000] [1,1000]中的数,我们枚举 [ 1 , 1000 ] [1,1000] [1,1000]中的质数为起点即可

代码

#include<bits/stdc++.h>
#define M 1000009
using namespace std;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<1)+(re<<3)+ch-'0';
	return re*f;
}
const int inf=1e9+7;
int pri[M],v[M],num[M],cnt,ans=inf,n;
int nxt[M],first[M],to[M],tot,dep[M];
void init(){
	for(int i=2;i<=1e6;i++){
		if(!v[i]) v[i]=i,pri[++cnt]=i;
		for(int j=1;j<=cnt;j++){
			if(i*pri[j]>1e6||v[i]<pri[j]) break;
			v[i*pri[j]]=pri[j];
		}
	}return;
}
void add(int x,int y){
	nxt[++tot]=first[x],first[x]=tot,to[tot]=y;
	nxt[++tot]=first[y],first[y]=tot,to[tot]=x;
}
void check(int x){
	int y=x,cnt1=0,d[5];
	while(y>1){
		num[v[y]]^=1;
		y/=v[y];
	}y=x;
	while(y>1){
		if(num[v[y]]) d[++cnt1]=v[y];
		num[v[y]]=0,y/=v[y];
	}if(!cnt1){printf("1\n");exit(0);}
	if(cnt1==1) add(1,d[1]);
	else add(d[1],d[2]);
}
void solve(){
	pri[0]=1;
	//printf("%d\n",cnt);
	for(int i=0;i<=cnt;i++){
		if(pri[i]>1000) break;
		memset(dep,-1,sizeof(dep));
		queue<pair<int,int> >q;
		q.push(make_pair(pri[i],0));
		dep[pri[i]]=0;
		//printf("%d\n",i);
		while(q.size()){
			int u=q.front().first,fa=q.front().second;
			q.pop();
			for(int j=first[u];j;j=nxt[j]){
				int v1=to[j];
				if(v1==fa) continue;
				if(dep[v1]>=0) ans=min(ans,dep[u]+dep[v1]+1);
				else dep[v1]=dep[u]+1,q.push(make_pair(v1,u));
			}
		}//printf("%d\n",ans);
	}if(ans!=inf) printf("%d\n",ans);
	else printf("-1\n");
	return;
}
int main(){
	init();
	n=read();
	for(int i=1;i<=n;i++){
		int x=read();
		check(x);
	}
	//for(int i=1;i<=tot;i+=2) printf("%d %d\n",to[i],to[i+1]);
	//for(int i=1;i<=20;i++) printf("%d\n",v[i]);
	solve();
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值