2020 icpc ec-final L.Sqare

题目大意:给出n个数字a1~an,求n个数字t1~tn,使得对任何的i>=1,i<n,都满足ai*ti*ai+1*ti+1是一个平方数,求t1*t2*........*tn的最小值

思路:对于出现的每一个质因子,在每个数中要么指数都是奇数,要么都是偶数,在奇数和偶数之间取2种操作的最小值就是答案,细节看代码。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N =1e6+10;
int mod=1e9+7;
int n,cnt,p[N],f[N],s[N],vis[N],hav[N],num[N];
long long ksm(long long a,long long b)//快速幂,这里的a,b要注意设成long long类型,因为后面的a*a会爆int 
{
	long long res=1;
	while(b)
	{
		if(b&1)
		res=res*a%mod;
		b=b>>1;
		a=a*a%mod; 
	}
    return res;
}
//复习一下欧拉筛求素数 
void getprimes()
{
	s[1]=1;
	for(int i=2;i<N;i++)
	{
		if(!f[i])	
		p[++cnt]=i,s[i]=i;//如果i在之前未被标记,那i就一定是质数 
	    //s[i]记录的是i这个数字的最小质因子 
		for(int j=1;j<=cnt;j++)
		{
			if(i*p[j]>=N)//如果当前得到的数字超出了范围直接break 
			break;
			f[i*p[j]]=1;//素数的倍数一定不是素数,可以直接标记 
			s[i*p[j]]=p[j];
			if(i%p[j]==0)//这句话是欧拉筛的精髓,如果i能被p[j]整除,那么在之后的i*p[j+1]....都会被p[j]*一个数所标记
//	现在来证明一下:当i是p[j]的整数倍的时候,记m=i/p[j],则i*p[j+1]=m*p[j+1]*p[j],说明i*p[j+1]是p[j]的倍数吗,不需要再进行标记 (在之后会被 prime[j] *某个数(m*prime[j+1])标记),
//对于 prime[j+2] 及之后的素数同理,直接跳出循环,这样就避免了重复标记。		 
			break;
		}
	}
}
int main()
{
	getprimes();
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		int tot=0;
		while(x!=1)
		{
			if(!vis[s[x]])
			hav[++tot]=s[x];
			vis[s[x]]++;
			x/=s[x];
		}//质因数分解并将质因数的指数存放在vis数组中,hav数组存的是该数可以分解为哪些质数 
		for(int j=1;j<=tot;j++)
		{
			if(vis[hav[j]]&1)
			num[hav[j]]++;//num数组记录的是指数为奇数的质因子的出现次数,num[i]=x,指的是i作为一个质因子在给定的n个数字中出现且其指数为奇数的出现次数为X 
			vis[hav[j]]=0;//vis数组要记得清空,以便为计算下一个数做准备 
		}
	}
	long long ans=1;
	for(int i=1;i<=cnt;i++)
	{
		if(!num[p[i]])
		continue;
		ans=ans*ksm(p[i],min(num[p[i]],n-num[p[i]]))%mod;//这里着重解释一下ksm(p[i],min(num[p[i]],n-num[p[i]]))的意思,这里的num[p[i]]的意思是说对所有含质因子p[i],且其指数为奇数的数字,每个数字乘一个p[i],使得每相邻的两个数的乘积中p[i]一项的指数为偶数
		//n-num[p[i]]的意思是说在所有数中那些p[i]作为质因子出现偶数次的数字乘一个p[i],使它们的指数都变成奇数,相邻两个数相乘p[i]的指数也就都变成了偶数 ,在这2种操作中取一个较小值即可 
	}
	cout<<ans<<endl;
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值