11.16 校内模拟赛解题报告

为什么还要考试啊,我的天啊!!!!

T1

二分 + 贪心。

可以发现,一行的数是连续的肯定会让这一行最大值减去最小值最小。

对数组进行排序,进行二分答案,用连续的数铺每一行,看能否铺满。
来个好看的代码。

int k, n, m, ans, a[maxn]; 
bool check(int mid)
{
	int js = 0;
    for(int i = 1; i + m - 1 <= k; i++)
        if(a[i + m - 1] - a[i] <= mid) js++, i += m - 1;
    if(js >= n) return 1;
    return 0;
}
int main()
{
	k = read(), n = read(), m = read();
	if(m == 1) {puts("0"); return 0;}
	for(int i = 1; i <= k; i++) a[i] = read();
	sort(a + 1, a + k + 1);
	int l = 0, r = a[k];
	while(l <= r)
	{
		int mid = (l + r) >> 1;
		if(check(mid)) r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	printf("%d", ans);
	return 0;
}

T2

60 pts

\(n ^3\) 的DP。

\(f[i][j]\) 表示前 \(i\)​ 个数分成 \(j\) 段所得到的最大价值。

\(f[i][j] = max(f[k][j - 1] + sum[k + 1][i])\)​。

	n = read(), K = read();
	for(int i = 1; i <= n; i++) a[i] = read();
	for(int i = 1; i <= n; i++) 
		for(int j = i; j <= n; j++)
			sum[i][j] = (sum[i][j - 1] | a[j]);
	for(int i = 1; i <= n; i++) f[i][1] = sum[1][i];
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j < i; j++)
		{
			for(int k = 1; k <= Min(i, K); k++)
			{
				f[i][k] = Max(f[i][k], f[j][k - 1] + sum[j + 1][i]);
			}	
		}
	}
	printf("%d", f[n][K]);

100 pts

可以发现,\(sum\) 数组有一段一段的数是相同的。

\(f\) 数组肯定是单调递增的,对于每个固定的 \(i\) 点,\(sum[k+1][j]\) 的取值最多只有 \(32\)​ 种。

预处理出转移点。

int n, K, a[maxn], sum[maxn][maxn], f[maxn][1000]; // 把前 i个数分成 j块 
vector<int> e[maxn];
int main()
{
	//freopen("b.in", "r", stdin);
	//freopen("b.out", "w", stdout);
	n = read(), K = read();
	for(int i = 1; i <= n; i++) a[i] = read();
	for(int i = 1; i <= n; i++) 
		for(int j = i; j <= n; j++)
			sum[i][j] = (sum[i][j - 1] | a[j]);
	for(int i = 1; i <= n; i++)
	{
		e[i].push_back(i);
		for(int j = i - 1; j >= 1; j--)
		if(sum[j][i] != sum[j + 1][i]) e[i].push_back(j); 
	}
	for(int i = 1; i <= n; i++)
	{
		for(int k = 1; k <= Min(i, K); k++)
		{
			for(int j = 0; j < e[i].size(); j++)
			{
				int v = e[i][j];
				f[i][k] = Max(f[i][k], f[v - 1][k - 1] + sum[v][i]);	
			}
		}	
	}
	printf("%d", f[n][K]);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T3

30pts

暴力枚举全排列。

60pts

对于这棵二叉树设最下边一层为第 \(0\)​ 层,设编号 \(i\) 与编号 \(j\) 在第 \(k\) 层相遇,设 \(i < j\)

对于第 \(k\)​ 层,每个点对应的叶子节点为 \(2 ^ k\)

对于\(i\)​ 的子树,现在已知最大的一个编号为 \(i\)​, 还剩下 \(2^k - 1\)​ 个节点,那么这棵子树中所有的情况就是 \(C(2^k - 1, i - 1)\)​​ 。

对于 \(j\)​​ 的子树,以为已知 \(i < j\)​ ,所以 \(i\)​ 子树内所有的编号一定小于 \(j\)​ 的子树内的,所以 \(j\)​ 的子树内编号为 \(j - 1 - 2 ^ k\)​,所有的情况就是 \(C(2 ^k - 1, j- 1 - 2^k )\)

上边讨论的是编号的情况,又因为编号是随机排列的,所以子树内的编号的排列顺序也是不固定的,还要乘以两个\(2 ^k\) !。

对于一整颗二叉树,除了刚刚讨论的 \(i, j\) 子树中的点, 还有 \(n - 2 ^{k + 1}\) 个点, 这些点的排列也是随机的,所以再再乘以一个 \(n - 2 ^{k + 1}\)​ !。

刚刚讨论的是一个点对,要算出这一层中有多少个点对,就是 \(2 ^{K- k - 1}\)​​个。

因为这是 \(i < j\) 的情况, 还有 \(i > j\) 的情况,所以还要乘以 \(2\)

最后再乘上 \(i, j\) 的权值。

枚举所有的 \(i, j\) ,它们的总贡献就是

\[\sum_{k = 0}^{K - 1}2^{K - k} \times (n - 2^{k + 1})! \times 2^{k}! \times 2^{k}! \times \displaystyle\sum_{i=1}^{n} \sum_{j = i + 1}^{n}\binom{i-1}{x} \times \binom{j-1 - 2^{k}}{x} \times a_i \times a_j \]

100 pts

考虑前面这个东西做一个前缀和。

\[\displaystyle\sum_{i=1}^{n} \binom{i-1}{x} \times a_i \sum_{j = i + 1}^{n} \binom{j-1 - 2^{k}}{x}\times a_j \]

时间复杂度为 \(O(k 2 ^k)\)​。

隔壁 zxs AC代码。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 3e5 + 5;
const int MOD = 1e9 + 7;
int read() {
	int x = 0, f = 1;char c = getchar();
	while(c < '0' || c > '9' ) {if (c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
	return x * f;
}
int K, Val[MAXN], N, Fac[MAXN], ret, inv[MAXN], sum[MAXN];
int Qpow(int x, int y) {
	int ret = 1;
	while(y) {
		if(y & 1) ret = (ret * x) % MOD;
		y >>= 1;
		x = (x * x) % MOD;
	}
	return ret;
}
int C(int n, int m) {
	if (m > n) return 0;
	return Fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
signed main() {
	//freopen("c4.in", "r", stdin);
	//freopen("c.out", "w", stdout);
	K = read();
	N = (1 << K), Fac[0] = 1, inv[0] = 1;
	for (int i = 1; i <= N; i++) Fac[i] = Fac[i - 1] * i % MOD;
	for (int i = 1; i <= N; i++) inv[i] = Qpow(Fac[i], MOD - 2);
	for (int i = 1; i <= N; i++) Val[i] = read();
	for (int k = 0, tmp; k < K; k++) {
		tmp = (1 << (K - k)) * Fac[N - (1 << k + 1)] % MOD;
		tmp = tmp * Fac[(1 << k)] % MOD * Fac[(1 << k)] % MOD;
		int all = 0;
		for (int j = N; j >= 1; j--) {
		  sum[j] = (sum[j + 1] + C(j - 1 - (1 << k), (1 << k) - 1) * Val[j] % MOD) % MOD;
		}
		for (int i = 1; i <= N; i++) 
		  all = (all + C(i - 1, (1 << k) - 1) % MOD   * Val[i] % MOD * sum[i + 1] % MOD) % MOD;
		tmp = tmp * all % MOD;
		ret = (ret + tmp) % MOD;
	}
	int tot = Fac[N];
	cout<<ret * Qpow(tot, MOD - 2) % MOD;
	return 0;
}
posted @ 2021-11-16 17:29  _程门立雪  阅读(44)  评论(1编辑  收藏  举报