2019 ICPC 网络赛(上海)D

题目链接:D. Counting Sequences I

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

S = ( n 1 a 1 , n 2 a 2 . . . . . n k a k ) S= (n_1*a_1,n_2*a_2.....n_k*a_k) 是由 n 1 a 1 , n 2 a 2 . . . . . . n k a k n_1个a_1,n_2个a_2......n_k个a_k 组成的多重集。S的全排列个数位
n ! n 1 ! n 2 ! . . . n k ! \frac{n!}{n_1!n_2!...n_k!}
n = n 1 + n 2 + . . . + n k n=n_1+n_2+...+n_k

思路

假设我们构造的序列的乘积为f,因为 2 13 > 3000 2^{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;
}


发布了70 篇原创文章 · 获赞 5 · 访问量 7161

猜你喜欢

转载自blog.csdn.net/xiaonanxinyi/article/details/100862326
今日推荐