3 条题解

  • 9
    @ 2026-10-8 19:43:47

    Rectangles

    O(n2)\mathcal{O}(n^2) 做法(嘲讽 Sam O(n2log⁡n)\mathcal{O}(n^2\log n))。

    HDU 2013。

    给定 n,m,kn,m,k 以及 kk 个关键点 (Xi,Yi)(X_i,Y_i)。求有多少个 1≤x1≤x2≤n,1≤y1≤y2≤m1\le x_1\le x_2\le n,1\le y_1\le y_2\le m,满足矩形 (x1,y1)(x2,y2)(x_1,y_1)(x_2,y_2) 内部至少有一个关键点。

    1≤n,m≤109,1≤k≤10001\le n,m\le 10^9,1\le k\le 1000。

    首先我们有一个错误的做法:

    对于每一个关键点 (Xi,Yi)(X_i,Y_i),计算所有包含这个点的矩形个数,即 XiYi(n−Xi+1)(m−Yi+1)X_iY_i(n-X_i+1)(m-Y_i+1) 个矩形。

    然后将所有关键点的答案累加。

    错误的原因是,一个矩形可能包含多个点,会被重复计算很多次。

    于是为了保证不算重,我们在计算关键点 (Xi,Yi)(X_i,Y_i) 时,不妨钦定关键点 ii 为包含其的矩形中编号最大的。这样每一个矩形就只会在其编号最大的点处被计算。

    为了方便,我们将所有关键点按 XiX_i 为第一关键字,YiY_i 为第二关键字排序并去重。

    那么枚举 ii,计算时我就需要计算所有不包含编号 >i>i 的矩形个数。

    接下来有一个错误但是具有启发性的做法:

    由于所有不能被矩形包含的关键点都在 ii 的右边或者上面,于是我们得出结论:(x1,y1)(x_1,y_1) 可以在 1∼Xi1\sim X_i 和 1∼Yi1\sim Y_i 内随便取(注意这里有问题)。

    于是被限制的就只有 (x2,y2)(x_2,y_2) 了。

    然后我们又发现,一个编号 j(j>i)j(j>i) 的关键点被包含。当且仅当 x2≥Xj,y2≥Yjx_2\ge X_j,y_2\ge Y_j,这可以看成是一个矩形覆盖,所有被覆盖的点都不能被选择。

    那么我们可以使用单调栈处理这些点的面积并,然后拿总面积减去即可。

    然而上面的实际上有问题:对于那些编号 j(j>i)j(j>i) 的且 Yj<YiY_j<Y_i 的点,这些点实际上会影响 (x1,y1)(x_1,y_1)!(自己画图)。

    具体的,不妨设 x2,y2x_2,y_2 已经确定,那么对于那些 j>ij>i 的 jj 且 Yj<YiY_j<Y_i,那么如果满足 Yj≥y1Y_j\ge y_1,那么这个矩形就会包含 jj!于是,尽管 x1x_1 没有限制,但是 y1y_1 必须 ≥max⁡Yj<Yi∧j>i{Yj}\ge\max\limits_{Y_j<Y_i\land j>i}\{Y_j\}。

    那么在上面处理 x2,y2x_2,y_2 的单调栈的过程中,我们需要同步维护一个指针 pp 维护所有 Yj<YiY_j<Y_i 的 jj,以及 pp 以前的最大值 MM。那么如果上面单调栈得到的 (x2,y2)(x_2,y_2) 的方案数为 SS,那么总答案就会累加 SXi(Yi−M)SX_i(Y_i-M)。

    这样你就得到了暴打 O(n2log⁡n)\mathcal{O}(n^2\log n) 的 O(n2)\mathcal{O}(n^2) 做法。

    • 2
      @ 2026-10-8 21:56:54

      题意:给定 n,mn,m 和平面上的 kk 个点,数有多少个矩形(左下 (x1,y1)(x_1,y_1)、右上 (x2,y2)(x_2,y_2),要求 1≤x1≤x2≤n1\le x_1\le x_2\le n、1≤y1≤y2≤m1\le y_1\le y_2\le m)里至少含一个给定点,答案对 109+710^9+7 取模。

      最直白的做法是把所有矩形都枚举一遍。先按输入把点打到一张二维前缀和表上,这样判断「某个矩形里有没有点」就是 O(1)O(1) 的;然后四重循环枚举 x1≤x2x_1\le x_2 与 y1≤y2y_1\le y_2,有就累加。

      代价是 Θ(n2m2)\Theta(n^2m^2),可以过测试点 1,21,2,20 分。

      直接数「至少包含一个给定点的矩形」不好数,改成按 x 方向分列枚举。

      把 kk 个点的 xx 坐标去重排序,得到若干列 v1<v2<⋯<vtv_1<v_2<\dots<v_t(再加两个哨兵 v0=0v_0=0、vt+1=n+1v_{t+1}=n+1)。矩形的左边界 x1x_1 落在哪两条相邻列之间、右边界 x2x_2 落在哪两条之间,一共只有 O(t2)O(t^2) 种「列区间」可以枚举。

      固定一段列区间(矩形至少要横跨第 ii 到第 jj 列里的点)之后再看 y 方向:

      • 左边界 x1x_1 只能在 (vi−1,vi](v_{i-1},v_i] 里取,共 vi−vi−1v_i-v_{i-1} 种;
      • 右边界 x2x_2 只能在 [vj,vj+1)[v_j,v_{j+1}) 里取,共 vj+1−vjv_{j+1}-v_j 种;
      • 中间这些列的点,按 yy 从小到大依次插入有序集合。插入一个点 yy 时设它在已插入集合里的前驱、后继分别是 b,tb,t,那么以这个点为最低点、且 y 区间内至少含一个点的 (y1,y2)(y_1,y_2) 对数正好是 (t−y)(y−b)(t-y)(y-b)。

      把所有点这样累加起来,每一对合法的 (y1,y2)(y_1,y_2) 都会被「它包含的最低那个点」统计恰好一次,不重不漏。于是这段列区间的贡献就是

      $$(\text{y 方向的合法区间数})\times(v_i-v_{i-1})\times(v_{j+1}-v_j)$$

      对所有列区间求和即可。

      列数不超过 k+1k+1,每段列区间的 y 方向扫描用有序集合做,总复杂度 O(k2log⁡k)O(k^2\log k)。

      • @ 2026-10-9 11:56:14

        流程:

        • 固定左右范围之后,从左向右扫描区间内的竖直直线。
        • 在集合中加入哨兵 y=0y = 0 和 y=m+1y=m+1。
        • 对于一条竖直直线,按照 y 从小到大枚举点。
        • 对于当前 y,找出集合中前驱 b、后继 t。以 y 为最下点的矩形上下界组合有 (t−b)(y−b)(t-b)(y-b) 个。将其加入答案。
          • 为什么不是 (m−b+1)(y−b)(m-b+1)(y-b) 呢?这是为了不重复计算。
          • 假设在 t 计算结束之后“以 t 为最低点的所有合法矩形上下边匹配情况” 都已经被计算了。那么所有下底边(0,t](0, t] 匹配 上底边[t,m+1)[t, m+1) 都已经被计算了。
          • 下底边(b,y](b, y] 匹配 上底边[y,t)[y, t) 是没有被算过的。但是,下底边 (b,y](b,y] 匹配 上底边[t,m+1)[t, m+1) 都已经被计算过了。
        • 最后将当前点加入集合。
    • 2
      @ 2026-10-8 19:32:22

      2782矩形计数题解

      题意

      一个 n×mn \times m 的平面直角坐标系,并给定 kk 个点,求至少包含一个点的矩形数量 n≤109n \le 10^9,m≤109m \le 10^9,k≤1000k \le 1000

      思路

      首先,不难注意到时间复杂度是 kk 的平方级左右(比如O(k2×logk)O(k^2\times log{k})或O(k2)O(k^2))

      考虑二维问题,常见定一维,处理一维,所以就枚举一个 k2k^2 的上下边界,然后发现另一维度可以由二维dp解决

      #include <bits/stdc++.h>
      using namespace std;
      using uint = unsigned int;
      using ll = long long;
      using ull = unsigned long long;
      template<class T, class U>
      inline bool chmax(T& a, const U& b)
      {
          if (a < b)
          {
              a = b;
              return true;
          }
          return false;
      }
      template<class T, class U>
      inline bool chmin(T& a, const U& b)
      {
          if (a > b)
          {
              a = b;
              return true;
          }
          return false;
      }
      constexpr size_t N = 1005;
      constexpr int MOD = 1000000007;
      using point = array<int, 2>;
      int n, m, k;
      point a[N];
      int sorted[N], s;
      ll dp[N][N];
      int main()
      {
          ios::sync_with_stdio(false);
          cin.tie(nullptr);
          freopen("rectangle.in", "r", stdin);
          freopen("rectangle.out", "w", stdout);
          cin >> n >> m >> k;
          for (int i = 1; i <= k; i++)
          {
              cin >> a[i][0] >> a[i][1];
              sorted[i] = a[i][0];
          }
          sort(a + 1, a + k + 1);
          set<int> st;
          for (int i = 1; i <= k; i++)
          {
              st.clear();
              dp[i][i] = a[i][1] * ll(m - a[i][1] + 1) % MOD;
              st.insert(a[i][1]);
              for (int j = i + 1; j <= k; j++)
              {
                  int y = a[j][1];
                  auto it = st.lower_bound(a[j][1]);
                  if (it != st.end() && *it == y)
                  {
                      dp[i][j] = dp[i][j - 1];
                      continue;
                  }
                  int pre = (it == st.begin() ? 0 : *prev(it));
                  if (it != st.end() && *it == a[j][1])
                  {
                      it++;
                  }
                  int nxt = (it == st.end() ? m + 1 : *it);
                  dp[i][j] = (dp[i][j - 1] + (nxt - y) * ll(y - pre)) % MOD;
                  st.insert(a[j][1]);
              }
          }
          sort(sorted + 1, sorted + k + 1);
          s = unique(sorted + 1, sorted + k + 1) - sorted - 1;
          sorted[s + 1] = n + 1;
          int res = 0;
          for (int i = 1; i <= s; i++)
          {
              int x1 = lower_bound(a + 1, a + k + 1, point{sorted[i], 0}) - a;
              ll dx1 = sorted[i] - sorted[i - 1];
              for (int j = i; j <= s; j++)
              {
                  int x2 = lower_bound(a + x1, a + k + 1, point{sorted[j + 1], 0}) - a - 1;
                  ll p = dx1 * (sorted[j + 1] - sorted[j]) % MOD;
                  res = (res + p * dp[x1][x2]) % MOD;
              }
          }
          cout << res << '\n';
          return 0;
      }
      

      有问题请直接在评论区@我

      • 1

      信息

      ID
      2782
      时间
      1000ms
      内存
      128MiB
      难度
      7
      标签
      (无)
      递交数
      61
      已通过
      11
      上传者