2025-04-06 模拟赛总结 😭
预期:\(100+100+40=240\)。
实际:\(40+40+40=120\)。
排名:\(rk(2\sim 5)/8\)。
比赛链接:http://yl503.yali.edu.cn/d/HEIGETWO/homework/67f237dd26e800f58b5154b6。
A - 平均数 / ave:
题意:
给定一个长度为 \(n(1\le n\le 10^5)\) 的正整数数列 \(a\),定义一个子区间的价值为其所有数的平均值,求所有子区间的代价的第 \(k(1\le k\le\frac{n(n+1)}2)\) 大。
思路:
区间 \([l,r]\) 平均值为 \(\dfrac{\sum_{i=l}^ra_i}{r-l+1}=\dfrac{\sum_{i=l}^ra_i}{\sum_{i=l}^r1}\),看到这种形式想到分数规划,所以二分答案 \(x\),\(C(x)\) 表示子区间平均值大于等于 \(x\) 的个数是否大于等于 \(k\),求满足 \(C(x)\) 最小的 \(x\) 即可。
考虑如何快速求出个数,\(\dfrac{\sum_{i=l}^ra_i}{\sum_{i=l}^r1}\ge x\Longleftrightarrow\sum_{i=l}^r(a_i-x)\ge 0\),运用前缀和求区间逆序对数(但是可以相等)即可求出个数。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 1e5 + 5;
const double eps = 1e-6;
int n, a[kMaxN], t[kMaxN], c[kMaxN], tot;
long long k;
double s[kMaxN], b[kMaxN], L, R, M;
void Modify(int x, int y) {
for (int i = x; i <= tot; i += i & -i) {
c[i] += y;
}
}
int Query(int x, int r = 0) {
for (int i = x; i; i -= i & -i) {
r += c[i];
}
return r;
}
bool C(double x, long long ret = 0) {
tot = 0;
for (int i = 0; i <= n; i++) {
s[i] -= i * x, b[++tot] = s[i];
}
sort(b + 1, b + 1 + tot), tot = unique(b + 1, b + 1 + tot) - b - 1;
for (int i = 0; i <= n; i++) {
t[i] = lower_bound(b + 1, b + 1 + tot, s[i]) - b;
s[i] += i * x;
}
fill(c, c + 1 + tot, 0);
for (int i = 0; i <= n; i++) {
ret += Query(tot) - Query(t[i] - 1), Modify(t[i], 1);
}
return ret >= k;
}
int main() {
freopen("ave.in", "r", stdin);
freopen("ave.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i], s[i] = s[i - 1] + a[i];
}
L = 0, R = 1e9, M = (L + R) / 2;
for (; R - L > eps; M = (L + R) / 2) {
C(M) ? R = M : L = M;
}
cout << fixed << setprecision(4) << L;
return 0;
}
反思:
一开始想复杂了,没看到 \(k\) 的范围想到了超级钢琴,后面才发现可以直接二分,这里浪费了 1h。还是要仔细看题,不要想当然。
(还有出题人为什么要卡常啊!!!!!)
B - 涂色游戏 / color:
题意:
现在有一个 \(n\times m\) 的网格图,每个颜色都可以涂上 \(1\sim p\) 中的任意一个颜色,求不单调的涂色方案的方案数。
我们定义一个涂色方案是不单调的,当且仅当任意相邻两列都出现了至少 \(q\) 种颜色。
这道题 \(n,p,q\le 100,m\le 10^9\)。
思路:
首先考虑暴力 dp,设 \(f_{i,j}\) 表示填到第 \(i\) 列,第 \(i\) 列恰好有 \(j\) 种不同的元素的不单调的涂色方案,再考虑预处理 \(g_i\) 表示颜色为 \(1\sim i\) 都用上填 \(n\) 个格子的方案数。
由于我们知道去掉限制都用上的方案数为 \(i^n\),用容斥即可算出 \(g\)。求出 \(g\) 后只需要管颜色的选择,不需要管颜色的排列,将颜色选择数乘以相应的 \(g\) 就可以算出总方案数。
那么 \(f_{1,i}\) 就是用 \(i\) 种颜色随便填,答案就是 \(C_p^ig_i\)。
考虑 \(f_{i,j}\gets f_{i-1,k}\),由乘法原理只需将 \(f_{i-1,k}\times t_{k,j}\) 累加到 \(f_{i,j}\) 即可,现在只需要求出 \(t_{k,j}\) 即可。
-
若 \(k\ge q\):随便填即可,\(t_{k,j}=C_p^jg_j\)。
-
若 \(k<q\) 且 \(i<q-j\):显然凑不出 \(q\) 种颜色,\(t_{k,j}=0\)。
-
若 \(k<q\) 且 \(i\ge q-j\):可以枚举出现了 \(l\) 个与前一列不同的颜色,那么显然 \(l\in[q-k,p-k]\),那么不与前一列重复颜色的选择数为 \(C_{p-k}^l\),与前一列重复的颜色的选择数为 \(C_k^{i-l}\)。所以 \(t_{k,j}=g_j\sum C_{p-k}^lC_k^{i-l}\)。
会暴力 dp 后,用矩阵快速幂加速即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 105, kM = 998244353;
int n, m, p, q, fac[kMaxN], finv[kMaxN];
long long g[kMaxN], ans;
long long fpow(long long a, long long b, long long p, long long r = 1) {
for (a = (a % p + p) % p; b; b & 1 && (r = r * a % p), b >>= 1, a = a * a % p) {
}
return r % p;
}
long long C(int n, int m) { return n > m || n < 0 || m < 0 ? 0 : 1LL * fac[m] * finv[n] % kM * finv[m - n] % kM; }
struct M {
int n, m;
long long a[kMaxN][kMaxN];
M(int h = 0, int w = 0) { n = h, m = w, memset(a, 0, sizeof(a)); }
long long* operator[](int x) { return a[x]; }
M operator*(M b) const {
M r(n, b.m);
for (int i = 1; i <= n; i++) {
for (int k = 1; k <= m; k++) {
for (int j = 1; j <= b.m; j++) {
r[i][j] = (r[i][j] + a[i][k] * b[k][j]) % kM;
}
}
}
return r;
}
} F, Ft;
M Mpow(M a, long long b) {
M r(a.n, a.m);
for (int i = 1; i <= a.n; i++) {
r[i][i] = 1;
}
for (; b; b >>= 1, a = a * a) {
if (b & 1) r = r * a;
}
return r;
}
int main() {
freopen("color.in", "r", stdin);
freopen("color.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> p >> q, fac[0] = finv[0] = 1;
for (int i = 1; i < kMaxN; i++) {
fac[i] = 1LL * fac[i - 1] * i % kM;
finv[i] = fpow(fac[i], kM - 2, kM);
}
for (int i = 1; i <= p; i++) {
g[i] = fpow(i, n, kM);
for (int j = 1; j < i; j++) {
g[i] = (g[i] - g[j] * C(j, i) % kM + kM) % kM;
}
} // 容斥计算 g
F = M(1, p), Ft = M(p, p);
for (int i = 1; i <= p; i++) {
for (int j = 1; j <= p; j++) {
if (j >= q) {
Ft[j][i] = C(i, p) * g[i] % kM;
} else if (i >= q - j) {
for (int k = q - j; k <= p - j; k++) {
Ft[j][i] = (Ft[j][i] + C(k, p - j) * C(i - k, j)) % kM;
}
Ft[j][i] = Ft[j][i] * g[i] % kM;
}
}
}
for (int i = 1; i <= p; i++) {
F[1][i] = C(i, p) * g[i] % kM;
}
F = F * Mpow(Ft, m - 1);
for (int i = 1; i <= p; i++) {
ans = (ans + F[1][i]) % kM;
}
cout << ans;
return 0;
}
反思:
求颜色填法可以转化为颜色选法乘 \(1\sim i\) 的排列方法,后者可以预处理得到,前者需要根据题目考虑计算。
这一题少写了 1LL * 导致挂成暴力分,下次需要检查 long long 了。(如果保险的话也可以写 #define int long long)
C - 序列 / seq:
题意:
给定长度为 \(n\) 的序列 \(a\),再给出 \(m\) 个三元组 \((l_i,r_i,x_i)\),定义序列 \(a\) 的价值为所有三元组价值之和,一个三元组的价值为 \(j\in[l_i,r_i]\) 中 \(a_j\ge x\) 的个数。又有 \(q\) 次操作,每次操作单点修改 \(a\),你在每次操作后输出序列 \(a\) 的价值,强制在线。
本题 \(n,m,q\le 10^5\)。
思路:
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 2e5 + 5;
int n, m, q, a[kMaxN], c[kMaxN], tot, root[kMaxN], lc[kMaxN << 5], rc[kMaxN << 5];
long long last, sum[kMaxN << 5];
vector<pair<int, int>> v[kMaxN]; // 0 : + / 1 : -
int NewNode() { return ++tot; }
void Update(int &u, int v, int l, int r, int p, int k) {
u = NewNode(), lc[u] = lc[v], rc[u] = rc[v], sum[u] = sum[v] + k;
if (l == r) return;
int mid = l + r >> 1;
p <= mid ? Update(lc[u], lc[v], l, mid, p, k) : Update(rc[u], rc[v], mid + 1, r, p, k);
}
long long Query(int u, int l, int r, int L, int R) {
if (L <= l && r <= R) return sum[u];
int mid = l + r >> 1;
long long ret = 0;
L <= mid && (ret += Query(lc[u], l, mid, L, R));
R > mid && (ret += Query(rc[u], mid + 1, r, L, R));
return ret;
}
int main() {
freopen("seq.in", "r", stdin);
freopen("seq.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1, l, r, x; i <= m; i++) {
cin >> l >> r >> x;
v[l].push_back({x, 1}), v[r + 1].push_back({x, -1});
}
root[0] = NewNode();
for (int i = 1; i <= n; i++) {
root[i] = root[i - 1];
for (pair<int, int> j : v[i]) {
int tmp = root[i];
Update(root[i] = 0, tmp, 1, n, j.first, j.second);
}
}
for (int i = 1; i <= n; i++) {
last += Query(root[i], 1, n, 1, a[i]);
}
cout << last << '\n';
for (long long p, v; q; q--) {
cin >> p >> v, p ^= last, v ^= last;
last -= Query(root[p], 1, n, 1, a[p]);
last += Query(root[p], 1, n, 1, a[p] = v);
cout << last << '\n';
}
return 0;
}

浙公网安备 33010602011771号