题解:P17217 [ICPC 2017 Nanning R] Five Dimensional Discrete Fourier Transform
lailai0916 · · 题解
题意简述
五维数组的元素由五个下标的异或值与一个可分离的复相位组成。求其五维离散傅里叶变换中,所有频率点实部绝对值之和,再按数组大小归一化。
解题思路
设第
若
由于每个
原式中的相位同样能够按维度分离。令:
则:
异或展开后的常数项与四个乘积项,都是五个一维函数的乘积。离散傅里叶变换具有线性性,而且可分离数组的五维变换等于五个一维变换的乘积。
对每个维度
再对四个二进制位预处理:
于是任意频率点
代码不为每个频率点重复解码五维坐标,而是逐维扩展一个张量。开始时只有一个元素,其五个分量分别放入系数:
加入第
输入的 remainderl 约化角度,并在全部三角函数和复数运算中使用 long double。
记
正确性证明
符号函数的乘积在五个对应位中有偶数个
相位指数是五个只依赖单一下标的项之和,所以复指数等于五个一维因子的乘积。异或展开中的每一项也具有相同的可分离形式。对可分离项展开五重 DFT 求和时,可以依次把五个独立求和提出,得到五个一维 DFT 的乘积。因此,频率点公式与题目定义的五维 DFT 完全相同。
逐维张量扩展时,归纳假设旧元素的第
程序对每个频率点累加实部绝对值,最后除以
参考代码
#include <bits/stdc++.h>
using namespace std;
using ld=long double;
using cd=complex<ld>;
const int K=5;
const int N=10;
const ld pi=acosl(-1);
const ld c[K]={7.5L,-0.5L,-1,-2,-4};
const int sgn[K]={1,-1,1,-1,1};
cd w[K][K][N];
void init(int d,int n,ld a)
{
for(int i=0;i<K;i++)
{
for(int j=0;j<n;j++)
{
w[d][i][j]=0;
for(int k=0;k<n;k++)
{
ld s=i&&((k>>(i-1))&1)?-1:1;
ld x=(sgn[d]*a-2*pi*j/n)*k;
w[d][i][j]+=s*cd(cosl(x),sinl(x));
}
}
}
}
void mul(array<cd,K> &a,const array<cd,K> &b,int d,int x)
{
for(int i=0;i<K;i++)a[i]=b[i]*w[d][i][x];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
cout<<fixed<<setprecision(6);
while(T--)
{
int n[K],siz=1;
for(int i=0;i<K;i++)
{
cin>>n[i];
siz*=n[i];
}
ld a;
cin>>a;
a=remainderl(a,2*pi);
for(int i=0;i<K;i++)init(i,n[i],a);
vector<array<cd,K>> f(1),g;
for(int i=0;i<K;i++)f[0][i]=c[i];
for(int i=0;i<K;i++)
{
g.resize(f.size()*n[i]);
for(int j=0;j<f.size();j++)
{
for(int k=0;k<n[i];k++)mul(g[j*n[i]+k],f[j],i,k);
}
f.swap(g);
g.clear();
}
ld ans=0;
for(auto &v:f)
{
cd z=0;
for(int i=0;i<K;i++)z+=v[i];
ans+=fabsl(z.real());
}
cout<<ans/(siz*sqrtl(siz))<<'\n';
}
return 0;
}