AtCoder Regular Contest 104 E Random LIS

洛谷传送门

AtCoder 传送门

首先期望转化成 \(\text{LIS}\) 总和除以方案总数(即 \(a_i\) 乘积)不必多说了。观察可发现题目 \(n\) 特别小,考虑 \(O(n^n)\) 枚举 \(x_i\) 的相对大小关系(排名),固定排名后算出 \(\text{LIS}\),再计算这种排名对应的方案数。

于是现在问题变成了有 \(m\) 个数,每个数的上界为 \(b_i\),要求序列单调递增的方案数。考虑直接套 CF1295F Good Contest 的做法,\(b_i\) 离散化后设 \(f_{i,j}\) 为第 \(i\) 个数位于值域第 \(j\) 段的方案数。转移枚举以 \(i\) 为右端点的极长值域位于同一段的区间左端点 \(k\),以及 \(k-1\) 位于哪个段。系数是在 \([1,len_j]\) 中选 \(i-k+1\) 个互不相同的数的方案数(为 \(\binom{len_j}{i-k+1}\))。可得:

\[f_{i,j} \gets \sum\limits_{k=1}^{i-1} \sum\limits_{x=1}^{j-1} \binom{len_j}{i-k+1} f_{k-1,x} \]

暴力计算即可。

总时间复杂度大概是 \(O(n^{n+5})\)(没仔细算,实际远远达不到)。

code
// Problem: E - Random LIS
// Contest: AtCoder - AtCoder Regular Contest 104
// URL: https://atcoder.jp/contests/arc104/tasks/arc104_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 12;
const ll mod = 1000000007;

inline ll qpow(ll b, ll p) {
	ll res = 1;
	while (p) {
		if (p & 1) {
			res = res * b % mod;
		}
		b = b * b % mod;
		p >>= 1;
	}
	return res;
}

ll n, fac[maxn], ifac[maxn], a[maxn], b[maxn], c[maxn], g[maxn], ans;
ll h[maxn], lsh[maxn], f[maxn][maxn];

inline ll C(ll n, ll m) {
	if (n < m || n < 0 || m < 0) {
		return 0;
	} else {
		ll ans = ifac[m];
		for (int i = 0; i < m; ++i) {
			ans = ans * (n - i) % mod;
		}
		return ans;
	}
}

inline ll work() {
	mems(h, 0x3f);
	ll m = 0;
	for (int i = 1; i <= n; ++i) {
		h[b[i]] = min(h[b[i]], a[i]);
		m = max(m, b[i]);
	}
	int tot = 0;
	for (int i = 1; i <= m; ++i) {
		lsh[++tot] = h[i];
	}
	lsh[++tot] = 0;
	sort(lsh + 1, lsh + tot + 1);
	tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
	for (int i = 1; i <= m; ++i) {
		h[i] = lower_bound(lsh + 1, lsh + tot + 1, h[i]) - lsh;
	}
	mems(f, 0);
	f[0][1] = 1;
	for (int i = 1; i <= m; ++i) {
		for (int j = 2; j <= h[i]; ++j) {
			// printf("%d %d\n", i, j);
			ll mn = h[i];
			for (int k = i; k; --k) {
				mn = min(mn, h[k]);
				if (j > mn) {
					break;
				}
				for (int x = 1; x < j; ++x) {
					if (!f[k - 1][x]) {
						continue;
					}
					f[i][j] = (f[i][j] + C(lsh[j] - lsh[j - 1], i - k + 1) * f[k - 1][x] % mod) % mod;
				}
			}
		}
	}
	ll ans = 0;
	for (int i = 2; i <= tot; ++i) {
		ans = (ans + f[m][i]) % mod;
	}
	// if (ans) {
		// printf("b: ");
		// for (int i = 1; i <= n; ++i) {
			// printf("%lld ", b[i]);
		// }
		// putchar('\n');
		// printf("h: ");
		// for (int i = 1; i <= m; ++i) {
			// printf("%lld ", h[i]);
		// }
		// putchar('\n');
		// printf("%lld\n", ans);
	// }
	return ans;
}

void dfs(int d) {
	if (d > n) {
		for (int i = 1; i <= n; ++i) {
			c[i] = b[i];
		}
		sort(c + 1, c + n + 1);
		for (int i = 1; i <= n; ++i) {
			if (c[i] - c[i - 1] > 1) {
				return;
			}
		}
		ll mx = 0;
		for (int i = 1; i <= n; ++i) {
			g[i] = 1;
			for (int j = 1; j < i; ++j) {
				if (b[j] < b[i]) {
					g[i] = max(g[i], g[j] + 1);
				}
			}
			mx = max(mx, g[i]);
		}
		ans = (ans + mx * work() % mod) % mod;
		return;
	}
	for (int i = 1; i <= n; ++i) {
		b[d] = i;
		dfs(d + 1);
	}
}

void solve() {
	scanf("%lld", &n);
	fac[0] = 1;
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
		fac[i] = fac[i - 1] * i % mod;
	}
	ifac[n] = qpow(fac[n], mod - 2);
	for (int i = n - 1; ~i; --i) {
		ifac[i] = ifac[i + 1] * (i + 1) % mod;
	}
	dfs(1);
	// printf("ans: %lld\n", ans);
	for (int i = 1; i <= n; ++i) {
		ans = ans * qpow(a[i], mod - 2) % mod;
	}
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2023-04-15 22:28  zltzlt  阅读(15)  评论(0编辑  收藏  举报