【BZOJ】2277: [Poi2011]Strongbox

题意

有一个密码箱,\(0\)\(n-1\)中的某些整数是它的密码。如果\(a\)\(b\)都是它的密码,那么\((a+b)%n\)也是它的密码(\(a,b\)可以相等)。某人试了\(k\)次密码,前\(k-1\)次都失败了,最后一次成功了。该密码箱最多有多少不同的密码。

分析

假设集合\(s\)为答案,则令\(g=gcd(s_i)\),则显然\(kg, k \ge 0\)都是答案。一共有\(\frac{n}{g}\)个。
所以我们找一个最小的\(g\),满足\(g|n, g|a_n, g \nmid a_i(i < n)\)即可。

题解

首先求出\(g=gcd(n, a_n)\)的所有约数。首先将等于\(a_i(i < n)\)的约数去掉,然后从小到大枚举。如果\(b_i * p_j\)被去掉了,显然\(b_i\)也要被去掉。然后一直做下去就行了。复杂度\(O(n^{0.5}log^2n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll getint() {
	ll x=0;
	int c=getchar();
	for(; c<48||c>57; c=getchar());
	for(; c>47&&c<58; x=x*10+c-48, c=getchar());
	return x;
}
const int M=1000005, N=250005;
inline ll gcd(ll a, ll b) {
	return b?gcd(b, a%b):a;
}
ll a[N], b[M], c[M], n;
int K, cnt, tot;
bool no[M];
int main() {
	ll n=getint();
	int K=getint();
	for(int i=1; i<=K; ++i) {
		a[i]=getint();
	}
	ll g=gcd(a[K], n), z;
	for(z=1; z*z<g; ++z) {
		if(g%z==0) {
			b[tot++]=z;
			b[tot++]=g/z;
		}
	}
	if(z*z==g) {
		b[tot++]=z;
	}
	sort(b, b+tot);

	ll t=g;
	for(z=2; z*z<=t; ++z) {
		if(t%z==0) {
			c[cnt++]=z;
			for(t/=z; t%z==0; t/=z);
		}
	}
	if(t!=1) {
		c[cnt++]=t;
	}

	for(int i=1; i<K; ++i) {
		ll x=gcd(a[i], g);
		no[lower_bound(b, b+tot, x)-b]=1;
	}
	for(int i=tot-1; i>=0; --i) {
		if(no[i]) {
			continue;
		}
		ll x=b[i];
		for(int j=0; j<cnt && x<=g/c[j]; ++j) {
			ll y=x*c[j];
			int k=lower_bound(b, b+tot, y)-b;
			if(b[k]==y && no[k]) {
				no[i]=1;
				break;
			}
		}
	}
	for(int i=0; i<tot; ++i) {
		if(!no[i]) {
			printf("%lld\n", n/b[i]);
			return 0;
		}
	}
	return 0;
}
posted @ 2015-11-22 13:44  iwtwiioi  阅读(432)  评论(0编辑  收藏  举报