2019 ICPC 網絡賽(上海)D

題目鏈接:D. Counting Sequences I

前置技能:多重集的排列數

S=(n1a1,n2a2.....nkak)S= (n_1*a_1,n_2*a_2.....n_k*a_k)是由n1a1,n2a2......nkakn_1個a_1,n_2個a_2......n_k個a_k組成的多重集。S的全排列個數位
n!n1!n2!...nk!\frac{n!}{n_1!n_2!...n_k!}
n=n1+n2+...+nkn=n_1+n_2+...+n_k

思路

假設我們構造的序列的乘積爲f,因爲213>30002^{13}>3000,所以可以肯定這個對於構造的序列來說非1的數不會太多,最多也就是10多個,此外f是小於等於n*2的,具體證明不是很清楚,但是打表出來的結果確實是這樣,於是猜了一下過了。所以說只要我們能夠找出非1的數的大小及數量,那麼就可以用多重集的排列數計算了。

由於它的個數不會太多,並且也不大(小於2*n),所以可以用搜索取逐一搜索出來。

搜索是一門藝術

AC_CODE

時間比預期的還優秀,200ms+就A了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
ll n;
ll a[3000], cnt, f[1000];
ll ans;

ll fspow(ll x, ll n) {
	ll ans = 1;
	while (n) {
		if (n & 1)ans = ans * x%mod;
		n >>= 1;
		x = x * x%mod;
	}
	return ans;
}

void init() {
	f[0] = 1;
	for (int i = 1; i < 1000; i++)f[i] = f[i - 1] * i%mod;
}

void solve(int k) {
	ll res = 1;
	for (int i = n - k + 1; i <= n; i++)res = res * i%mod;
	int c = 0;
	a[0] = a[1]; a[cnt + 1] = a[cnt] + 1;
	for (int i = 1; i <= cnt + 1; i++) {
		if (a[i] == a[i - 1])c++;
		else {
			res = res * fspow(f[c], mod - 2) % mod;
			c = 1;
		}
	}
	ans = (ans + res) % mod;
}

void dfs(int st, int k, int f, int sum) {
	if (f > 2 * n)return;
	if (sum + n - k > 2 * n)return;
	if (f == sum + n - k) {
		solve(k);
		return;
	}
	for (int i = st; i <= n && f*i <= 2 * n; i++) {
		a[++cnt] = i;
		dfs(i, k + 1, f*i, sum + i);
		cnt--;
	}
}

int main() {
	init();
	int t; scanf("%d", &t);
	while (t--) {
		cnt = 0;
		scanf("%lld", &n);
		ans = 0;
		dfs(2, 0, 1, 0);
		printf("%lld\n", ans);
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章