TopCoder SRM 546 Div1 Hard FleaCircus

問題

長さnの順列(1〜nの数の並び替え)を、
4回適用した順列P^4が与えられる。


このとき、Pとしてありうる順列は何通りか、求めよ。

制約条件

n≦700くらい

方針

まずは順列をサイクルに分割する。


Pにおいて長さkのサイクルが、P^4ではどうなっているかを考察する。
すると、

  • k = 4l のとき、lの長さのサイクル4つに分かれる
  • k = 4l±1 のとき、4l±1のサイクルのまま
  • k = 4l + 2 のとき、2l + 1の長さのサイクル2つに分かれる

ことがわかる。


したがって、サイクル長が奇数のときは、同じ長さが1個か2個か4個集まったサイクルから分解されて出来ていて、
サイクル長が偶数のときは、同じ長さのサイクル4つが集まったサイクルから分解されて出来ていることがわかる。


これがわかったら、サイクル長ごとに、
その個数のサイクルを作る場合の数をDPで求めればいい。
サイクルの長さがiで、個数がnum[i]個だとする。
サイクルk個を作る場合の数をdp[k]とすると、
iが奇数のとき、dp[k] = Σdp[k - {1, 2, 4}] * その合併の仕方
iが偶数のとき、dp[k] = dp[k - 4] * その合併の仕方


m個を合併するときの仕方は、まず1個の選び方は固定でよくて、
残りのm-1個の選び方がP(num[i]-1, m-1)通り。
次にエッジのつなぎ方がi ^ (m-1)通り。

ソースコード

const int mod = (int)1e9 + 9;
int n, to[1000], num[1000], dp[1000];
bool v[1000];

int dfs(int c){
	v[c] = 1;
	int res = 1;
	if(!v[to[c]]) res += dfs(to[c]);
	return res;
}

class FleaCircus {
	public:
	int countArrangements(vector <string> afterFourClicks) 
	{
		string s;
		each(i, afterFourClicks) s += *i;
		stringstream ss(s);
		int t;
		n = 0;
		while(ss >> t) to[n++] = t;
		
		memset(v, 0, sizeof(v));
		memset(num, 0, sizeof(num));
		rep(i, n) if(!v[i]) num[dfs(i)]++;
		
		ll ans = 1;
		rep(i, n + 1) if(num[i]){
			vi g;
			if(i % 2) g.pb(1), g.pb(2), g.pb(4);
			else g.pb(4);
			
			memset(dp, 0, sizeof(dp));
			dp[0] = 1;
			for(int j = 1; j <= num[i]; j++) rep(k, g.size()){
				if(g[k] > j) break;
				ll tmp = 1;
				rep(l, g[k] - 1) tmp = tmp * (j - 1 - l) % mod;
				rep(l, g[k] - 1) tmp = tmp * i % mod;
				dp[j] = (dp[j] + tmp * dp[j - g[k]]) % mod;
			}
			ans = (ans * dp[num[i]]) % mod;
		}
		
		return ans;
	}
};