【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 mid 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/b[i]);
			return 0;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/iwtwiioi/p/4985737.html