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;
}

反思:

posted @ 2025-04-07 19:54  liruixiong0101  阅读(37)  评论(0)    收藏  举报