解法
自力で解けなかったので 解説 (youtube)を参考にした.
素数 が任意の整数 に対して を満たすかを判定する.フェルマーの小定理 より を満たすので, を剰余とした演算のもとでは は 次の整数係数多項式
となる.一般に,複素係数の 次多項式は 個の複素根を持つ(代数学の基本定理とその初等的な証明 | 高校数学の美しい物語).このとこから, 次の整数係数多項式は高々 個以下の整数根しか持つことができない.したがって,任意の整数 に対して が の根( が の倍数)となるための必要十分条件は,各係数が の倍数となることである.以上から,素数 に対して が答えに含まれるかを 時間で判定することができた.
次に,解の候補となる素数の範囲を調べる.演算を の剰余のもとで行うとき,多項式 は 次の多項式となったので, のときは各係数が の倍数となるかを判定することと等しくなる.また, が保証されているので調べる素数は 以下の素数と, 以上の の素因数のみで十分である.
計算時間: ( は の約数の個数;)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Prime {
int N;
vector<bool> is_p;
Prime(int _N) : N(_N), is_p(N + 1, true) {
is_p[0] = is_p[1] = false;
for (long long i = 2; i <= N; ++i) {
if (is_p[i])
for (long long j = i * i; j <= N; j += i)
is_p[j] = false;
}
}
bool operator[](const int idx) const { return is_p[idx]; }
};
template<class T>
std::vector<std::pair<T, T>> PrimeFactorization(T n) {
std::vector<std::pair<T, T>> pf;
T m = n;
for (T i = 2; i * i <= n; ++i) {
if (m % i != 0) continue;
T cnt = 0;
while (m % i == 0) { ++cnt; m /= i; }
pf.emplace_back(std::make_pair(i, cnt));
}
if (1 < m) pf.emplace_back(std::make_pair(m, 1));
return pf;
}
inline ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
bool Check(const ll p, const ll n, const vector<ll> &a) {
if (a[0] % p != 0) return false;
for (ll s = 0; s <= min(n, p - 2); ++s) {
ll sum = 0;
for (ll x = s; x <= n; x += p - 1) sum += a[x];
if (sum % p != 0) return false;
}
return true;
}
int main() {
cin.tie(0); ios::sync_with_stdio(false);
ll n;
cin >> n;
vector<ll> a(n + 1);
for (int i = n; 0 <= i; --i) cin >> a[i];
Prime primes(n);
set<ll> ans_p;
for (ll p = 2; p <= n; ++p) {
if (primes[p] && Check(p, n, a)) ans_p.insert(p);
}
auto pf = PrimeFactorization<ll>(abs(a[n]));
for (auto it : pf) if (Check(it.first, n, a)) ans_p.insert(it.first);
for (auto p : ans_p) cout << p << '\n';
return 0;
}