1 条题解

  • 0
    @ 2025-11-27 9:10:30

    好的,根据你提供的代码,我来整理出对应的题解算法标签


    题目解法概述

    本题要求计算所有 2N2^N 个子集的连通块数的 KK 次方之和。
    直接枚举子集不可行,需要利用动态规划 + 线段树进行高效统计。


    核心思路

    1. 排序与离散化

    • 将线段按左端点 lil_i 升序排序。
    • 坐标范围是 12N1 \dots 2N,不需要离散化,直接用线段树覆盖这个区间。

    2. 动态规划状态设计

    dp[r][j]dp[r][j] 表示处理到当前阶段,以 rr 为最右端点的所有子集的状态中,连通块数的 jj 次方之和(0jK0 \le j \le K)。

    3. 转移方法

    考虑加入一条新线段 [l,r][l, r]

    情况 1:线段与前面不重叠(左端点大于之前某些线段的右端点)

    • 从所有右端点 <l< l 的状态转移过来。
    • 新线段单独形成一个连通块,所以连通块数 +1+1
    • 利用二项式定理: [ (x+1)^j = \sum_{u=0}^j \binom{j}{u} x^u ] 这里 xx 是原来的连通块数,+1+1 后取 jj 次方,可以拆成组合数求和。

    情况 2:线段与前面重叠(右端点落在某个区间内)

    • 从右端点 [l,r]\in [l, r] 的状态转移。
    • 新线段与之前的线段合并,连通块数不变。
    • 直接继承 dp[r][j]dp[r'][j]

    情况 3:新增空集 + 当前线段

    • 相当于只选当前线段,连通块数为 11,贡献 1j=11^j = 1

    4. 线段树维护

    • 用线段树维护每个右端点对应的 dp[r][0K]dp[r][0 \dots K]
    • 支持:
      • 区间查询(用于情况 1 和情况 2)
      • 单点更新(更新新线段的右端点对应的状态)
      • 区间乘法(当新线段右端点大于某个旧右端点时,旧状态可以选或不选新线段,所以 ×2\times 2

    算法步骤

    1. 读入数据,排序。
    2. 初始化组合数 C[k][k]C[k][k]
    3. 建立线段树,覆盖 [1,2N][1, 2N]
    4. 遍历每条线段 [li,ri][l_i, r_i]
      • 查询 [1,li1][1, l_i-1] 得到不重叠的贡献,用二项式定理更新。
      • 查询 [li,ri][l_i, r_i] 得到重叠的贡献,直接累加。
      • 加上只选当前线段的贡献 11
      • 将新状态插入到 rir_i 位置。
      • [ri+1,2N][r_i+1, 2N] 区间 ×2\times 2(表示这些右端点对应的子集可选/不选当前线段)。
    5. 最终查询所有右端点的 dp[.][K]dp[.][K] 之和,输出。

    复杂度分析

    • 排序:O(NlogN)O(N \log N)
    • 线段树操作:每次 O(logN)O(\log N),且每次更新涉及 O(K2)O(K^2) 的组合计算。
    • 总复杂度:O(NK2logN)O(NK^2 \log N),在 N105,K10N \le 10^5, K \le 10 时可接受。

    代码关键点

    • bb 结构体存储 val[0K]val[0 \dots K] 表示 KK 次方和。
    • 线段树支持区间乘、单点更新、区间查询。
    • 组合数预处理用于二项式展开计算 (x+1)K(x+1)^K

    • 1

    信息

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