1 条题解

  • 0
    @ 2026-4-17 11:46:29

    题解:Access Levels 问题重述 有 nn 个开发者和 mm 份文档,用一个 n×mn \times m 的 0‑1 矩阵 aa 表示访问需求:ai,j=1a_{i,j}=1 表示开发者 ii 必须 能访问文档 jjai,j=0a_{i,j}=0 表示开发者 ii 必须不能 访问文档 jj

    我们可以进行以下操作:

    选择访问组的数量 k1k \ge 1

    为每份文档指定一个访问组(1k1 \dots k)和一个所需访问级别(11091 \dots 10^9);

    为每个开发者指定 kk 个访问级别(每个组一个,11091 \dots 10^9)。

    开发者 ii 能访问文档 jj 当且仅当:在文档 jj 所在的组 gg 中,开发者的级别 \ge 文档的所需级别。

    目标:用最少的组 kk 实现给定的访问需求表,并输出任意一种合法的分配。

    核心观察 同一组内的文档必须满足“可比性”

    假设两份文档 xxyy 被分到同一个组,且它们的所需级别不同。不妨设文档 xx 的所需级别 >y> y 的所需级别。那么任何能访问 xx 的开发者(其级别 \ge xx 的级别)必然也能访问 yy(因为级别 \ge xx 的级别 >> yy 的级别)。因此对于所有开发者 ii,必须有 ai,x≥ai,y.a i,x≥a i,y . 即文档 xx 对应的列向量(长度为 nn)逐元素不小于文档 yy 的列向量。记作 vxvyv_x \ge v_y

    反之,如果两个文档的列向量不可比(存在 ii 使 vx[i]=1,vy[i]=0v_x[i]=1,v_y[i]=0,且存在 jj 使 vy[j]=1,vx[j]=0v_y[j]=1,v_x[j]=0),则它们不可能放在同一个组中,否则会出现循环要求(vxvyv_x \ge v_yvyvxv_y \ge v_x 不能同时成立)。

    因此,同一组内的文档对应的列向量必须构成一个全序(按 \ge 关系)。列向量完全相同的文档自然可以放在同一组,且可以赋予相同的所需级别。

    转化为最小链覆盖问题 将每份文档视为一个节点,节点 uu 的权值为其列向量 vuv_u。定义有向边 uvu \to v 当且仅当 uvu \neq vvuvvv_u \ge v_v(逐元素)。这是一个偏序关系,构成的图是一个 DAG(相等情况已合并)。

    我们需要将节点划分成最少的链(每条链是一个全序子集),每条链对应一个访问组。这就是经典的最小链覆盖问题。

    对于 DAG,最小链覆盖 == 节点数 - 最大匹配(拆点二分图上的匹配)。具体地:

    将每个节点 uu 拆成左部 uLu_L 和右部 uRu_R

    若原图有边 uvu \to v,则在二分图中连接 uLu_LvRv_R

    求最大匹配,匹配边数 MM,则最小链覆盖 =m2M= m_2 - M,其中 m2m_2 是节点数。

    处理相同列向量 如果两个文档的列向量完全相同,则它们之间互相有边(vuvvv_u \ge v_vvvvuv_v \ge v_u),从而形成环,不能直接使用 DAG 上的链覆盖。但我们可以将它们合并为一个节点,因为:

    它们必须放在同一个组中(否则若分在不同组,无法满足可比性?实际上可以分在不同组,但为了最小化组数,合并后更优;而且合并不会破坏可行性,因为我们可以给它们分配相同的所需级别)。

    合并后,图变为 DAG。

    具体做法:将所有文档的列向量收集起来,排序去重,得到 m2m_2 个不同的向量。每个原始文档对应其中一个类别。

    算法步骤

    1. 输入与压缩 读入 n,mn,mnn 行二进制字符串。将每份文档的列(长度为 nn)存储为字符串 b[j]。 去重:将 b 排序并 unique,得到 nw 数组(大小为 m2m_2),以及每个原始文档所属的类别编号 num[j]。

    2. 建图 对 i,j[0,m21]i,j \in [0,m_2-1],若 iji \neq j 且 nw[i] 的每一位 \ge nw[j] 的对应位,则在二分图左部 ii 与右部 jj 之间连边。 注意:由于已去重,iji \neq j 时不会出现双向边,图是 DAG。

    3. 求最大匹配 在二分图上运行匈牙利算法(或最大流)。得到匹配数组 mt:mt[r] = l 表示右部节点 rr 匹配左部节点 ll

    4. 重建路径 初始化 nxt[l] = -1,st[l] = true(表示左部节点是否为路径起点)。

    遍历所有匹配:若 mt[r] = l,则令 nxt[l] = r,且 st[r] = false(右部节点 rr 有前驱,不是起点)。

    此时所有左部节点中 st[l] == true 的就是各条路径的起点。

    1. 分配组号和所需级别 对每条路径从起点开始沿着 nxt 走,依次给路径上的节点分配:

    组号 gr[v] = t(tt00 开始递增);

    所需级别 req[v] = pos,其中 pos 从 22 开始递增(保证严格递增,且最小为 22,方便后面构造开发者的级别)。

    路径上的节点顺序就是所需级别递减的顺序(首节点所需级别最高)。

    1. 输出文档的分配 对于每个原始文档 jj00‑based),其类别为 num[j],输出:

    组号 gr[num[j]] + 1;

    所需级别 req[num[j]]。

    1. 构造开发者的访问级别 对于每个开发者 ii00‑based),初始化一个长度为 kk 的数组 l 全为 11。 遍历所有不同的文档类别 vv0m210 \dots m_2-1),如果 nw[v][i] == '1'(即开发者 ii 需要访问这类文档),则更新 l[gr[v]]=max(l[gr[v]],req[v]). 最后输出 l[0..k-1]。

    正确性说明:在一条链(一个组)中,由于所需级别严格递增且列向量单调递减,每个开发者的需求模式必然是一段连续的 1 后面全是 0(因为向量逐元素非增)。因此,他需要的文档恰好是链中所需级别最高的若干文档。设他需要的最后一个文档的所需级别为 RR,则 l=Rl = R 时:

    对所有需要的文档,级别 \ge 所需级别;

    对所有不需要的文档,所需级别 R+1>l\ge R+1 > l,故无访问。

    ll 设为所有需要文档的 req 最大值,正好满足要求。初始值 11 保证了当开发者不需要任何文档时,级别 11 小于所有所需级别(2\ge 2)。

    复杂度分析 去重:O(mlogmn)O(m \log m \cdot n)

    建图:O(m22n)O(m_2^2 \cdot n)m2m500m_2 \le m \le 500,最坏 5003=1.25×108500^3 = 1.25\times 10^8,但实际常数小且数据随机时可接受。

    匈牙利算法:O(m2E)=O(m23)O(m_2 \cdot E) = O(m_2^3)5003=1.25×108500^3 = 1.25\times 10^8,仍可运行。

    总复杂度:O(m3+m2n)O(m^3 + m^2 n),符合题目限制。

    • 1

    信息

    ID
    6554
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    1
    已通过
    1
    上传者