20250808 模拟赛

并非码力,并非模拟


题目描述

给定一个 n×mn×m 的二元矩阵,11 表示黑色单元格,00 表示白色单元格。每一秒,每个黑色单元格会将其上、下、左、右四个相邻的白色单元格染成黑色。

你可以 最多 将一个白色单元格变成黑色(将 00 变成 11),以最小化整个矩阵完全变黑所需要的时间,求出这个最短时间。

文件读入:color.in
文件读写:color.out

一种O(n)的check方法 目前很快

首先将所有初始黑色单元格 (‘1’) 同时入队,计算每个单元格到最近黑色点的最小曼哈顿距离 dist[i][j]。 然后二分答案 二分时间T,难点在于如何check check就是判断是否存在一个白色或任意单元 (x,y) 使得对所有 dist[i][j]>T 的点,都有 |x−i|+|y−j| ≤ T

n^2check很唐,所以对绝对值分讨

i+j−T≤x+y≤i+j+T

i−j−T≤x−y≤i−j+T

所以对于所有白色的点,我们记录 u_min = max(i + j - T)

u_max = min(i + j + T)

v_min = max(i - j - T)

v_max = min(i - j + T)

最后 我们要判断: 是否存在一个整数点 (x, y),使得 (u = x + y) 和 (v = x - y) 都落在对应的范围内? x 与 y 需要为整数,x = (u + v)/2, y = (u - v)/2 这意味着u 和 v 必须具有相同奇偶性(即 (u + v) 与 (u - v) 都是偶数)

我们检查 [u_min, u_max] 与 [v_min, v_max] 这两个区间中是否有某一对整数 u, v 有相同奇偶性即可。


问题描述

阿斯凯拉德(Askeladd)是维京海盗的领袖,他们正在寻找宝藏。在袭击了一个村庄后,他们收集了 nn 个宝藏,按顺序排列,价值分别为 a1,a2,…,ana1​,a2​,…,an​。

团队有一个协议:作为领袖,阿斯凯拉德将获得前 kk 个宝藏(价值为 a1,a2,…,aka1​,a2​,…,ak​),其余的海盗将分配剩下的宝藏。然而,由于时间已晚,分配被推迟到第二天早上。

阿斯凯拉德很狡猾,他秘密地试图在夜间重新排列宝藏,以获得更多的总价值。他只能交换两个相邻的宝藏。每次交换的成本为 cc 价值,因为存在被其他海盗发现的风险。阿斯凯拉德可以执行任意次数的交换(包括零次)。

阿斯凯拉德想要找到他能获得的最大利润,定义为:他获得的宝藏总价值减去交换的总成本。

文件读入:treasure.in
文件读写:treasure.out

我们钦定p1...pk表示选的,要把这k个弄到前k各位置的坐标。

那么答案就是

\[\sum _{j = 1} ^{k} a_{p_j} - c * \sum _{j = 1} ^{k} {p_i - j} \]

发现后面-j可以直接提出来,变成

\[\sum _{j = 1} ^{k} {a_{p_j} - c * p_i} + \sum _{j = 1} ^{k} k \]


前面直接对于每个p = i算然后排序取k大即可。


对于一个长度为 LL(LL 为奇数) 的数组 aa,定义它的中位数 median(a)median(a) 为 aa 中第 L+122L+1​ 大的数。

现在给你一个长度为 nn 的排列,对于每对满足 1≤i≤j≤1≤i≤j≤ 并且 j−i≡0j−i≡0 (mod(mod 2)2) 的 (i,j)(i,j),你需要计算 i×j×median(p[i...j])i×j×median(p[i...j])

输出所有值的总和。

文件读入:median.in
文件读写:median.out

下面给出一个基于「把每个值 m 当作中位数来统计」的 O(n²) 做法思路。核心思想是:

  1. 枚举中位数值 m
    由于 p 是 1…n 的排列,中位数一定落在这些值里,我们对 m 从 1 到 n 遍历。

  2. 找到 m 在数组中的位置 pos
    任何一个奇长子段要以 m 为中位数,子段必须包含 pos,且该子段中比 m 小的数和比 m 大的数个数相同。

  3. 构造左右前缀平衡数组
    定义一个映射

    \[ b[i] = \begin{cases} +1, & p[i] > m,\\ -1, & p[i] < m,\\ 0, & i = pos. \end{cases} \]

    那么对于任何包含 pos 的子段 [i…j],它以 m 为中位数等价于

    \[ \sum_{t=i}^j b[t] = 0. \]

    我们把这个和拆成 “左半段” 和 “右半段”:

    • 左半段的前缀和:\(s_L(k)=\sum_{t=k}^{pos-1}b[t]\),对应子段左端点 i=k+1。
    • 右半段的前缀和:\(s_R(k)=\sum_{t=pos+1}^{k}b[t]\),对应子段右端点 j=k.
  4. 按平衡值配对并加权
    记左边所有可能的 \((\Delta=s_L,\ \text{parity of }i)\) 统计为

    \[ \text{cntL}[\Delta][\pi] = \sum_{\substack{i\le pos\\i\equiv \pi\bmod2}}1,\quad \text{sumL}[\Delta][\pi] = \sum_{\substack{i\le pos\\i\equiv \pi\bmod2}}i \]

    同理右边有 \(\text{cntR},\ \text{sumR}\)(按 j 的奇偶分组),
    只有当左端 i 和右端 j 同奇偶,且平衡值相等 \(\Delta_L=\Delta_R\) 时,子段 [i…j] 才会是奇长且满足总和为 0。
    因此对每个平衡值 \(\Delta\) 和奇偶 \(\pi\in\{0,1\}\)

    \[ \sum_{i,j}i\cdot j = \bigl(\text{sumL}[\Delta][\pi]\bigr)\times\bigl(\text{sumR}[\Delta][\pi]\bigr). \]

    把所有平衡值、两种奇偶情况累加,再乘上当前中位数 m,就得到了所有以 m 作中位数的子段贡献:

    \[ \text{contrib}(m) = m \;\times\;\sum_{\Delta,\pi}\bigl(\text{sumL}[\Delta][\pi]\bigr)\times\bigl(\text{sumR}[\Delta][\pi]\bigr). \]

  5. 时间复杂度
    对每个 m 我们在 O(n) 时间里扫一遍左右前缀并维护哈希表,合并统计也 O(n)(平衡值种类上限 2n+1)。总体 O(n)×n = O(n²)。


小结

  • 利用「把每个元素当候选中位数」的「前缀和 + 哈希配对」思路,省去了每段插入、求第 k 大的 log 因子;
  • 通过「左边、右边统计 (i, j) 同奇偶且平衡值相等」来保证子段为奇长且中位数恰好是 m;
  • 最终在 O(n²) 内完成所有测试用例的计算。

给你一个长度为 nn 的排列,你需要从中选出一个最长的子序列(注意是子序列不是子串),满足该子序列中两端的值大于中间的所有值。

输出子序列的最大长度。

文件读入:sequence.in
文件读写:sequence.out

下面给出一个 \(O(n\log n)\) 的做法,思路分三步:


一、预处理后缀最左/最右出现位置

记原排列为 \(a[1\ldots n]\),并且预先建立一个数组

\[\mathit{pos}[v]=\{\,i\mid a_i=v\} \]

对于每个阈值 \(T\)(我们把“端点最小值”记为 \(T\)),我们需要知道所有值 \(\ge T\) 在序列中出现的最左和最右位置:

// 初始化:
L[n+1] = +∞;   R[n+1] = −∞;

// 逆序维护:
for T = n, n−1, …, 1:
    L[T] = min( L[T+1], pos[T] )
    R[T] = max( R[T+1], pos[T] )

这样,对任意阈值 \(T\),区间 \([L[T],\,R[T]]\) 恰好囊括了所有值 ≥ \(T\)


二、离线统计“小于 T”的元素个数

对于固定 \(T\),在子序列中,中间元素必须严格小于 \(T\),且它们的位置在 \((L[T],\,R[T])\) 之间。设

\[\text{small}_T \;=\;\bigl|\{\,i\mid L[T]<i<R[T],\;a_i < T\}\bigr|. \]

那么以两端点取任意两个 \(\ge T\) 的位置为端点,能取得的最长子序列长度至多

\[\text{small}_T + 2. \]

为了 离线 高效算出 \(\text{small}_T\),我们按值从小到大“开闸放水”——把所有值 \(<T\) 的位置依次插入一个长度为 \(n\) 的 Fenwick 树(BIT),用来维护“哪些位置是小于当前阈值的”。

  • 初始时,BIT 全 0;

  • 当我们将某个值 \(v\)(从 1 到 \(n\))“放入”时,就在 pos[v] 处做 +1 操作;

  • 在放入之前,BIT 上恰好存储了「所有值 < \(T\) 的位置」;于是即可在 \(O(\log n)\) 内回答区间 \([L[T],R[T]]\) 中有多少个小于 \(T\)

    small_T = BIT.query(R[T]−1) − BIT.query(L[T])
    

    (因为两端点本身都是 ≥T,不算入中间)

posted @ 2025-08-08 22:24  Dreamers_Seve  阅读(4)  评论(0)    收藏  举报