Loading

11.16 模拟赛题解报告

总结

期望: 100+ 100 + 30 = 230

实际:40 + 60 + 10 = 110

T1 二分边界取小了,挂分。

T2 数组开小,挂分。

T3 搜索实现常数太大,被干掉了。

T1 最小差异矩阵

solution

二分 + 贪心。

直接二分答案,然后从前到后扫一遍,扫到一个合法的区间,就取这个区间作为一行,然后继续向后找。可以证明这样是最优的。

code

#include<bits/stdc++.h>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
const int MAXN = 1e5 + 5;
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, n, m, a[MAXN], Ans;
bool Check(int x) {
  int cnt = 0;
  for (int l = 1, r = l + m - 1;;l = l + m, r = l + m - 1) {
  	 while(a[r] - a[l] > x) l++, r++;
  	 if(r > k) return false;
  	 cnt++;
  	 if(cnt == n) return true;
  }
}
int main() {  
  //freopen("a.in", "r", stdin);
  //freopen("a.out", "w", stdout); 
  k = read(), n = read(), m = read();
  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)) Ans = mid, r = mid - 1;
  	else l = mid + 1;
  }
  cout<<Ans;
  return 0;
}
/*
5 2 2
7 5 8 2 3
*/

T2 分割序列

solution

\(dp\)

60pts

\(n^3\)\(f_{i, j}\) 为前 \(i\) 个,分了 \(j\) 段的最大价值,转移直接枚举上一次分的位置就好了。

转移 \(f_{i, j} = \max\{f_{k, j - 1} + val_{k + 1, i}\}\)

100pts

\(20n^2\)

\(n ^ 2\) 处理出区间或和,\(val_{j, i}\)

其实决策点 \(k\) 只取那些满足 \(val[k][i] \neq val[k + 1][i]\)\(k\) 就好了。

可以证明这些 \(k\) 才是最优的决策点。

证明:

\(j\) 固定的时候,\(dp_{i, j}\)\(i\) 单调不降。

并且取 \(k + 1 \sim i\) 这些点最后一段的贡献都是相同的,选最靠后的一个点显然会使得 \(dp[k][j]\) 最大。

code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2021;
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 N, K, a[MAXN], val[MAXN][MAXN], f[MAXN][MAXN];
vector<int>vec[MAXN];
int main() {
  //freopen("b.in", "r", stdin);
  //freopen("b.out", "w", stdout);
  N = read(), K = read();
  for (int i = 1; i <= N; i++) val[i][i] = read();
  for (int i = 1; i <= N; i++) 
    for (int j = i + 1; j <= N; j++) val[i][j] = val[i][j - 1] | val[j][j];
  for (int i = 1; i <= N; i++) {
    vec[i].emplace_back(i);
    for (int j = i - 1; j >= 1; j--) 
      if(val[j][i] != val[j + 1][i]) vec[i].emplace_back(j);
  }
  for (int k = 1; k <= K; k++) {
  	for (int i = 1; i <= N; i++) {
  	  if(i < k) continue;
  	  for (int j = 0; j < vec[i].size(); j++) {
	     int o = vec[i][j];
	     if(o >= k)
	     f[i][k] = max(f[o - 1][k - 1] + val[o][i], f[i][k]);
	  }  	
	}
  }
  cout<<f[N][K];
  return 0;
}

T3 树的魔法值

solution

我们考虑枚举这个二叉树的层数以最底层为 \(0\)

假设我们枚举到了第 \(k\) 层,我们有两个点 \(i,j(i < j)\) ,我们算一下他们会造成多少贡献。 我们一共需要 \(2^{k}\) 个点,去掉当前子树内的根节点还剩 \(x = 2^{k}-1\) 个点。

考虑能有多少种情况。

\[\binom{i-1}{x} \times \binom{j-1 - 2^{k}}{x} \]

我们要枚举所有的 \(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 \]

复杂度 \(O(K(2^K)^2)\)

考虑后面可以维护一个后缀和。

\[\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(K2^K)\)

code

#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() {
	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:18  Dita  阅读(41)  评论(0编辑  收藏  举报