tozangezan's diary

勝手にソースコードをコピペして利用しないでください。

AOJ 2560: Point Distance

二次元になっているものを一列にしてFFTをする。後は重複回数をよく考えつつうまくFFTの表から答えを出す。

実行時間がかなりかかった。なぜだろう。

#include<stdio.h>
#include<algorithm>
#include<complex>
#include<vector>
#include<math.h>
#include<map>
using namespace std;
const double PI = 4.0*atan(1.0);
typedef complex<double> Complex;
const Complex I(0, 1);
void fft(int n, double theta, Complex a[]) {
  for (int m = n; m >= 2; m >>= 1) {
    int mh = m >> 1;
    for (int i = 0; i < mh; i++) {
      Complex w = exp(i*theta*I);
      for (int j = i; j < n; j += m) {
        int k = j + mh;
        Complex x = a[j] - a[k];
        a[j] += a[k];
        a[k] = w * x;
      }
    }
    theta *= 2;
  }
  int i = 0;
  for (int j = 1; j < n - 1; j++) {
    for (int k = n >> 1; k > (i ^= k); k >>= 1);
    if (j < i) swap(a[i], a[j]);
  }
}
int b[1025][1025];
Complex p[1<<22];
Complex q[1<<22];
int main(){
	int a;
	scanf("%d",&a);
	int cnt=0;
	for(int i=0;i<a;i++){
		for(int j=0;j<a;j++)scanf("%d",&b[i][j]);
	}
	int n=1;
	while(n<a*2)n*=2;
	for(int i=0;i<a;i++)for(int j=0;j<a;j++){
		cnt+=b[i][j];
		p[i*n+j]=q[(n-1-i)*n+(n-1-j)]=Complex(b[i][j],0);
	}
	fft(n*n,2*PI/n/n,p);
	fft(n*n,2*PI/n/n,q);
	
	for(int i=0;i<n*n;i++)p[i]=p[i]*q[i];
	fft(n*n,-2*PI/n/n,p);
	for(int i=0;i<n*n;i++)p[i]/=n*n;
	/*for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)printf("%.0f ",p[i*n+j].real());
		printf("\n");
	}*/
	double ret=0;
	long long sz=0;
	map<int,long long>m;
	for(int i=0;i<n*n;i++){
			int pi=i/n;
			int pj=i%n;
			if(pj>=n/2)continue;
			if(pj==0&&pi>=n/2)continue;
			long long v=(long long)(p[n*n-1-i].real()+0.5);
			if(i==0){v-=cnt;v/=2;}
			if(!v)continue;
			
			int I=min(pi,n-pi);
			int J=pj;
			int t=I*I+J*J;
			if(m.count(t)){
				m[t]+=v;
			}else{
				m[t]=v;
			}
			sz+=v;
			ret+=sqrt(t)*v;
	}
	printf("%.12f\n",ret/sz);
	int at=0;
	for(map<int,long long>::iterator it=m.begin();it!=m.end();it++){
		if(at==10000)break;
		at++;
		printf("%d %lld\n",(*it).first,(*it).second);
	}
}