https://www.acmicpc.net/problem/31939
문제 해석
먼저 문제를 변형해보자.
임의의 점 z=reiθ 에서 받는 힘의 크기는
k=1∏N∣z−αk∣2, αk=xk+iyk
이때
Q(z)=k=1∏N(z−αk)=k=0∑NCkzk
라고 두면
E[k=1∏N∣z−αk∣2]=πR21∫∣z∣≤R∣Q(z)∣2dA=k=0∑N∣Ck∣2k+1R2k
로 표현할 수 있다. 즉, 문제는 Ck의 제곱값을 구하는 문제로 환원할 수 있다.
증명과 풀이 과정
1. 셋업
천체의 좌표를 αk=xk+iyk∈C 로 두고
다항식 Q(z)를 정의하자. (앞서 정리한 것과 같다.) 그러면 임의의 점 z에서 힘의 크기는 ∣Q(z)∣2가 된다. 이제 구할 기댓값은
E[∣Q(Z)∣2]=πR21∫∫∣z∣≤R∣Q(z)∣2dA(z)
이때 Z는 원판 {z:∣z∣≤R} 위 균등분포이고, dA는 2차원 면적 요소를 의미한다.
Q는 다항식이므로 ∣Q∣2은 연속이고 유계함수이며, 원판이 유한 영역이므로 유한합과 적분의 교환이 정당하다.
2. 직교성
다항식의 제곱 절댓값을 전개하면
∣Q(z)∣2=(k=0∑NCkzk)(l=0∑NCˉlzˉl)=k, l=0∑NCkCˉlzkzˉl
따라서
∫∫∣z∣≤R∣Q(z)∣2dA=k, l=0∑NCkCˉl∫∫∣z∣≤RzkzˉldA
이제 원판에서의 단항식 직교성을 계산한다. 극좌표 z=reiθ에서
zkzˉl=rk+lei(k−l)θ, dA=r dr dθ
즉,
∫∫∣z∣≤RzkzˉldA=∫0R∫02πrk+lei(k−l)θr dr dθ=(∫0Rrk+l+1dr)(∫02πei(k−l)θdθ)
이때
∫02πei(k−l)θdθ={10k=lk=l
이므로 (푸리에함수의 완전직교성)
∫∫∣z∣≤RzkzˉldA=δkl ⋅ 2π∫0Rr2k+1dr=δkl ⋅ πk+1R2k+2
즉 원판에서 zk들은 서로 직교한다.
3. 기댓값의 닫힌형
직교성으로 교차항이 모두 사라지므로
∫∫∣z∣≤R∣Q(z)∣2dA=k=0∑N∣Ck∣2 ⋅ πk+1R2k+2
원판 평균을 취하면
E[∣Q(Z)∣2]=πR21∫∫∣z∣≤R∣Q(z)∣2dA=k=0∑N∣Ck∣2 ⋅ k+1R2k
답은 어떻게 구하나?
Q(z)의 계수 Ck=ak+bk를 구하면
∣Ck∣2=ak 2+bk 2를 통해 문제를 해결할수 있다.
이제 다시 유한곱 꼴로 돌아와서, 각 선형인수 (z−αk)를 계수가 Fp[k] 인 다항식으로 보자.
이때 (z−αk)=(1, 0)z+(−xk, −yk)로 두고 이것을 분할정복을 통해 product tree의 형태로 곱해주면 된다. 다항식 곱셈은 NTT를 사용하면 된다.
그러면 두 복소 다항식을 어떻게 NTT로 곱한다는걸까? 그 답은 간단하다. 두 다항식 Re[i], Im[i]을 한 개의 구조체로 묶어 복소 연산을 적용하면 된다. 이는 3번의 NTT로 해결할 수 있는데,
A(z)=A0(z)+iA1(z), B(z)=B0(z)+iB1(z)
A⋅B=(A0B0−A1B1)+i(A0B1+A1B0)
와 같이 두고,
S1S2S3=A0∗B0=A1∗B1=(A0+A1)∗(B0+B1)
세 가지의 합성곱을 구해주면, C(z)=C0+iC1을 다음과 같이 구할 수 있다.
C0C1=S1−S2=S3−S1−S2
그 구현은 다음과 같다.
struct CPoly {
vector<int> rea, ima;
CPoly(int n) {
rea.assign(n, 0);
ima.assign(n, 0);
}
size_t size() const { return rea.size(); };
void resize(size_t n) {
rea.assign(n, 0);
ima.assign(n, 0);
};
CPoly operator*(const CPoly &o) const {
auto S1=ntt.conv(rea, o.rea);
auto S2=ntt.conv(ima, o.ima);
vector<int> A(size()), B(o.size());
for (int i=0; i<size(); i++) A[i]=(1LL*rea[i]+ima[i])%MOD;
for (int i=0; i<o.size(); i++) B[i]=(1LL*o.rea[i]+o.ima[i])%MOD;
auto S3=ntt.conv(A, B);
int n=S3.size();
CPoly C(n);
for (int i=0; i<n; i++) {
C.rea[i]=(1LL*S1[i]-S2[i]+MOD)%MOD;
C.ima[i]=(1LL*S3[i]-S1[i]-S2[i]+MOD*2)%MOD;
}
return C;
}
};
CPoly make(int a, int b) {
CPoly P(2);
P.rea[0]=(MOD-a)%MOD;
P.ima[0]=(MOD-b)%MOD;
P.rea[1]=1;
P.ima[1]=0;
return P;
}