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\) 个点。
考虑能有多少种情况。
我们要枚举所有的 \(i,j\) ,则总贡献为
复杂度 \(O(K(2^K)^2)\)
考虑后面可以维护一个后缀和。
复杂度 \(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;
}