問題:見つける
主な課題は、大きくなる可能性のあるクエリ (Q) の処理です。1 <= Q <=
これまでに使用した方法:
強引な
while(Q--)
{
int N;
cin>>N;
for(int i=1;i<=N;i++)
ans += lcm(i,N)/i ;
}
最初に、すべての N のオイラー totient 関数の値を保持するテーブルを作成します。これは O(N) で実行できます。
void sieve()
{
// phi table holds euler totient function value
// lp holds the lowest prime factor for a number
// pr is a vector which contains prime numbers
phi[1]=1;
for(int i=2;i<=MAX;i++)
{
if(lp[i]==0)
{
lp[i]=i;
phi[i]=i-1;
pr.push_back(i);
}
else
{
if(lp[i]==lp[i/lp[i]])
phi[i] = phi[i/lp[i]]*lp[i];
else phi[i] = phi[i/lp[i]]*(lp[i]-1);
}
for(int j=0;j<(int)pr.size()&&pr[j]<=lp[i]&&i*pr[j]<=MAX;j++)
lp[i*pr[j]] = pr[j];
}
各クエリに対して N を因数分解d*phi[d]
し、結果に追加します。
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
// i is a factor
sum += (n/i)*phi[n/i];
if(i*i!=n)
{
// n/i is a factor too
sum += i*phi[i];
}
}
}
これには O(sqrt(N)) が必要です。
複雑さ : O(Q*sqrt(N))
O(1) でクエリを処理する
上で説明したふるい法に、必要な答えを O(NLogN) で計算する部分を追加します。
for(int i=1;i<=MAX;++i)
{
//MAX is 10^7
for(int j=i;j<=MAX;j+=i)
{
ans[j] += i*phi[i];
}
}
残念ながら、これは指定された制約と制限時間 (1 秒) でタイムアウトになります。
これには、 N の素因数分解に関する巧妙なアイデアが含まれていると思います。上記で作成した lp(lowest prime) テーブルを使用して O(LogN) の数値を素因数分解できますが、因数分解を使用して答えに到達する方法がわかりません。