BZOJ 2277 Poi2011 Strongbox 数论

42 篇文章 0 订阅

题目大意:给定n和k个整数,求mod n加法下的群G的一个子群G',满足a[1]~a[k-1]都不在群中而a[k]在群中


首先易证G'一定是一个循环群

证明:显然若a在群中则a的逆元在群中

那么我们就有了减法运算

由群的封闭性可得若a和b都在群中则gcd(a,b)一定在群中

不妨设g为G'中所有元素的gcd 那么群G''={0,g,2g,...}一定是G'的一个子群

由于G'-G''中的所有元素均不是g的倍数,故G'∩(G'-G'')为空 G'=G''

由此可得G'是以g为最小生成元的一个循环子群 大小为n/g


上面都是废话


总之我们现在就要找到一个数g,使得g|n且g|a[k],且对于任意1<=i<=k-1,g不是a[i]的约数

这个MS没有什么办法直接做


我们发现10^14以内的数约数个数最多只有17280个

因此我们将a[1]~a[k-1]中所有的数对n取gcd,排序去重,这样k的规模就减小到了17280

然后枚举gcd(n,a[k])的所有约数,暴力验证,取最小的g就是结果

时间复杂度O(√n+klogn+17280^2)

卡爆了


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 250250
using namespace std;
int k,tot;
long long n,ans,a[M];
void Check(long long x)
{
	int i;
	for(i=1;i<=tot;i++)
		if(a[i]%x==0)
			return ;
	ans=min(ans,x);
}
int main()
{
	int i;
	cin>>n>>k;ans=n;
	for(i=1;i<=k;i++)
	{
		#ifdef ONLINE_JUDGE
			scanf("%lld",&a[i]);
		#else
			scanf("%I64d",&a[i]);
		#endif
		a[i]=__gcd(n,a[i]);
	}
	sort(a+1,a+k);
	for(i=1;i<k;i++)
		if(i==k+1||a[i]!=a[i+1])
			a[++tot]=a[i];
	for(i=1;(long long)i*i<=a[k];i++)
		if(a[k]%i==0)
		{
			Check(i);
			Check(a[k]/i);
		}
	cout<<n/ans<<endl;
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值