[JOI精進録] 難易度7「fermat」(2007年日本情報オリンピック 春合宿3-2)

問題リンク

atcoder.jp

問題概要

x^n+y^n \equiv z^n \pmod pを満たすx,y,z (0 \le x,y,z \le p-1)の組はいくつ考えられますか?

解説

AC解法1 (100/100点)

繰り返し二乗法を使ってO(p \; log \; n)n乗の数をpで割ったあまりを列挙し、
A_i=n乗してpで割ったあまりがiになるxの数
を数え上げます。
xyについてAを全探索すると、zは定数時間で求められるためO(P^2)で答えることができます。

ACコード1

#include <bits/stdc++.h>
using namespace std;

int main(){
  int p,n; cin >> p >> n;
  long long r(0);
  vector<int> A(p);
  for (int i(0);i < p;++i){
    int r = 1;
    for (int t = n,u = i;t;t>>=1,u*=u,u%=p) if ((t&1)) r*=u,r%=p;
    ++A[r];
  }
  for (int i(0);i < p;++i) for (int k(0);k < p;++k){
    r += A[i]*A[k]*A[(i+k)%p];
  }
  cout << r << endl;
}

提出結果1

atcoder.jp

AC解法2(1より高速) (100/100点)

Aを1では全探索したところでFFTです。ACLに定義されているのを使うと簡単に実装できます。

ACコード2

#include <bits/stdc++.h>
#include <atcoder/convolution>
using namespace std;

int main(){
#define int long long
  int p,n,r; cin >> p >> n;
  vector<int> A(p);
  for (int i(0);i < p;++i){
    r = 1;
    for (int t = n,u = i;t;t>>=1,u*=u,u%=p) if ((t&1)) r*=u,r%=p;
    ++A[r];
  }
  vector<int> B = atcoder::convolution_ll(A,A);
  for (int i(0);i < B.size();++i) B[i]*=A[i%p];
  cout << accumulate(B.begin(),B.end(),0ll) << endl;
}

提出結果2

atcoder.jp