TopCoder SRM 592 Div1 Hard SplittingFoxes2

問題

n項からなる数列aがある。
これに対してn項からなる数列P={P[0], P[1], ...,P[n-1]}で表される操作を行うと、
a[0] := a[0] * P[0] + a[1] * P[n-1] + a[2] * P[n-2] + …
a[1] := a[0] * P[1] + a[1] * P[0] + a[2] * P[n-1] + …

のように変化する。
(aはaとPの巡回たたみこみになる)


ただし、PはP[i] = P[(n-i)%n]を満たすものとする。
Pを(1, 0, 0, ..., 0)に二回適用した後のaが与えられるとき、
Pとしてありえるもののうち、辞書順最小のものを求めよ。
ないときは-1を返せ。

制約条件

n≦25
a[i]≦1000000

方針

*をたたみこみの演算とすると、
a = P*P
なので、F(x)をxの離散フーリエ変換とすれば
F(a) = F(P) * F(P)(要素同士の通常の積)
より、
F(a)[i] = ±sqrt(F(P)[i])になる。


プラスマイナスの自由度が2^nあるが、
Pが偶関数(というのか?P[i] = P[(n-i)%n]のこと)
だとF(P)も偶関数になるという性質があるらしいので、自由度は2^(n/2+1)通りしかない。


ので、全通りプラスマイナスを試して、
逆変換したものが元に戻っているかを見ればよい。


フーリエ変換は要素が25しかないので、FFTを使わず愚直に計算して間に合う。
というか、偶関数性を使う都合上長さを2のべき乗ではなくnでフーリエ変換をしなくてはならないので、FFTだと大変。

ソースコード

class SplittingFoxes2 {
	public:
	vector <int> getPattern(vector <int> amount) {
		
		n = amount.size();
		int m = n / 2 + 1;
		rep(i, n) w[i] = polar(1.0, -2 * PI * i / n);
		
		vi ans;
		vector<P> x;
		
		rep(i, n) x.pb(amount[i]);
		x = dft(x);
		rep(i, n){
			double r = abs(x[i]);
			double t = arg(x[i]);
			
			x[i] = polar(sqrt(r), t / 2);
		}
		rep(i, n) w[i] = polar(1.0, 2 * PI * i / n);
		
		
		rep(i, 1 << m){
			vector<P> y = x;
			
			rep(j, n){
				if(j < m && (i & 1 << j) || j >= m && (i & 1 << (n - j)) ) y[j] *= -1.0;
			}
			y = dft(y);
			
			vi z;
			rep(j, n) z.pb((int)(y[j].real() / n + 0.48));
			if(ok(z, amount) && (ans.empty() || ans > z)) ans = z;
		}
		
		if(ans.empty()) ans.pb(-1);
		return ans;
	}
};