首页 > 其他分享 >P8164 [JOI 2022 Final] 沙堡 2 题解

P8164 [JOI 2022 Final] 沙堡 2 题解

时间:2024-10-24 20:43:24浏览次数:1  
标签:std tx ty 沙堡 题解 int vec P8164 mx

Description

JOI 君在沙滩上堆沙堡,他已经做好了一个沙堡,沙堡可以使用一个 \(H\times W\) 的二维矩形表示,其被划分成若干个 \(1\times 1\) 的小格子,格子高度互相不同。

JOI 君决定在沙堡上游走,他可以从任意一个点出发,向上下左右四个方向行走,必须满足他行走的路径单调下降。

出于一些原因,JOI 君想知道,在他所有可能的行走路径中,恰好覆盖了一个子矩形的路径数有多少条。

对于全部数据,\(H,W\ge 1\),\(HW\le 5\times 10^4\),\(1\le A_{i,j}\le 10^7\),\(A_{i,j}\) 互不相同。

Solution

首先一个子矩形合法的条件为子矩形内的每个点 \((i,j)\),都满足其值域上的前驱和后继都与其相邻。

但是直接暴力判断最多只做到 \(O\left(H^3W^2\right)\),过不了。

考虑对每个点构造一个权值,使得这些权值的和为一个定值。

令 \(X\) 为一个极大值,\(v_{i,j}\) 表示 \((i,j)\) 的权值,\(w_{i,j}\) 表示 \((i,j)\) 四周小于 \(a_{i,j}\) 的最大权值,如果没有则为 \(0\)。如果 \(a_{i,j}\) 大于其四周的数,则 \(v_{i,j}=X-w_{i,j}\),否则 \(v_{i,j}=a_{i,j}-w_{i,j}\)。

容易发现一个合法矩形的所有 \(v_{i,j}\) 的和为 \(X\),证明就考虑如果每个点向周围比它大的最大位置连边,会连出一个内向树森林,而 \(v_{i,j}\) 实际上是把每个内向树划分成若干条链,每条链的权值为 \(X\),所以总权值为 \(X\) 乘以链数,于是矩形合法的条件即为总权值为 \(X\)。

枚举矩形的上下边界和右端点,并在枚举的过程中维护每个位置的权值,用哈希表记录某个前缀的权值即可。

时间复杂度:\(O(H^2W)\)。

Code

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>

#define int int64_t

const int kMaxT = 5e4 + 5, kInf = 1e10;
const int kD[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

int n, m, t;
int unq[kMaxT], sum[kMaxT], valt1[kMaxT], valt2[kMaxT], valt3[kMaxT];
std::vector<std::vector<int>> a, val1, val2, val3;

void init(std::vector<std::vector<int>> &v, int n = ::n, int m = ::m) {
  v.resize(n + 2, std::vector<int>(m + 2));
}

int getid(int x) {
  return std::lower_bound(unq + 1, unq + 1 + t, x) - unq;
}

void discrete() {
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
      unq[++t] = a[i][j];
  std::sort(unq + 1, unq + 1 + t);
  t = std::unique(unq + 1, unq + 1 + t) - (unq + 1);
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
      a[i][j] = getid(a[i][j]);
}

void work() {
  std::swap(n, m);
  std::vector<std::vector<int>> b(n + 1, std::vector<int>(m + 1));
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
      b[i][j] = a[j][i];
  a = b;
}

int getval1(int l, int r, int x, int y) {
  int mx = 0, _mx = 0;
  for (auto [dx, dy] : kD) {
    int tx = x + dx, ty = y + dy;
    if (tx >= l && tx <= r && ty >= 1 && ty <= m) {
      if (a[tx][ty] > a[x][y]) mx = std::max(mx, a[tx][ty]);
      if (a[tx][ty] < a[x][y]) _mx = std::max(_mx, a[tx][ty]);
    }
  }
  if (!mx) mx = kInf;
  else mx = a[x][y];
  return mx - _mx;
}

int getval2(int l, int r, int x, int y) {
  int mx = 0, _mx = 0;
  for (auto [dx, dy] : kD) {
    int tx = x + dx, ty = y + dy;
    if (tx >= l && tx <= r && ty >= 1 && ty <= m && dy != -1) {
      if (a[tx][ty] > a[x][y]) mx = std::max(mx, a[tx][ty]);
      if (a[tx][ty] < a[x][y]) _mx = std::max(_mx, a[tx][ty]);
    }
  }
  if (!mx) mx = kInf;
  else mx = a[x][y];
  return mx - _mx;
}

int getval3(int l, int r, int x, int y) {
  int mx = 0, _mx = 0;
  for (auto [dx, dy] : kD) {
    int tx = x + dx, ty = y + dy;
    if (tx >= l && tx <= r && ty >= 1 && ty <= m && dy != 1) {
      if (a[tx][ty] > a[x][y]) mx = std::max(mx, a[tx][ty]);
      if (a[tx][ty] < a[x][y]) _mx = std::max(_mx, a[tx][ty]);
    }
  }
  if (!mx) mx = kInf;
  else mx = a[x][y];
  return mx - _mx;
}

void dickdreamer() {
  std::cin >> n >> m;
  init(a);
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
      std::cin >> a[i][j];
  if (n > m) work();
  init(val1), init(val2), init(val3);
  discrete();
  int64_t ans = 0;
  for (int l = 1; l <= n; ++l) {
    for (int i = 1; i <= m; ++i) {
      valt1[i] = val1[l][i] = getval1(l, l, l, i);
      valt2[i] = val2[l][i] = getval2(l, l, l, i);
      valt3[i] = val3[l][i] = getval3(l, l, l, i);
    }
    for (int r = l + 1; r <= n; ++r) {
      __gnu_pbds::gp_hash_table<int, int> mp;
      int sum = 0;
      for (int i = 1; i <= m; ++i) {
        val1[r][i] = getval1(l, r, r, i);
        valt1[i] += getval1(l, r, r - 1, i) - val1[r - 1][i] + val1[r][i];
        val2[r][i] = getval2(l, r, r, i);
        valt2[i] += getval2(l, r, r - 1, i) - val2[r - 1][i] + val2[r][i];
        val3[r][i] = getval3(l, r, r, i);
        valt3[i] += getval3(l, r, r - 1, i) - val3[r - 1][i] + val3[r][i];
        if (mp.find(kInf - sum - valt3[i]) != mp.end()) ans += mp[kInf - sum - valt3[i]];
        sum += valt1[i];
        ++mp[valt2[i] - sum];
      }
    }
  }
  for (int i = 1; i <= n; ++i) {
    int lst[2] = {-1, -1};
    std::vector<int> vec;
    for (int j = 1; j < m; ++j)
      vec.emplace_back(a[i][j] < a[i][j + 1]);
    ans += m;
    for (int j = 0; j < (int)vec.size(); ++j) {
      if (!vec[j]) {
        ans += j - lst[1];
        lst[0] = j;
      } else {
        ans += j - lst[0];
        lst[1] = j;
      }
    }
  }
  for (int i = 1; i <= m; ++i) {
    int lst[2] = {-1, -1};
    std::vector<int> vec;
    for (int j = 1; j < n; ++j)
      vec.emplace_back(a[j][i] < a[j + 1][i]);
    for (int j = 0; j < (int)vec.size(); ++j) {
      if (!vec[j]) {
        ans += j - lst[1];
        lst[0] = j;
      } else {
        ans += j - lst[0];
        lst[1] = j;
      }
    }
  }
  std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:std,tx,ty,沙堡,题解,int,vec,P8164,mx
From: https://www.cnblogs.com/Scarab/p/18500450

相关文章

  • 题解:Maximum AND
    ProblemLinkMaximumAND题外话用sort肘过去了?题面翻译给定数组\(a\)和\(b\),重排\(b\)数组,求\(a_i\oplusb_i\)之后与和的最大值。Solution不难想到按位贪心。从最高位开始,逐位讨论是否能为\(1\)。接下来有一个做法:先将\(a\)数组升序排序,\(b\)数组降序排......
  • AtCoder Regular Contest 185 题解
    A-modMGame2第一个观察是如果一个人手中还有2张牌,那么他一定不会被秒。这可以推出决定胜负的时刻一定是Alice和Bob手中只剩一张牌的时候,此时如果Alice被秒了,那么她就似了,否则她就赢了。考虑Alice什么时候能被秒。记决定胜负的时刻Alice手中的牌是\(a\),Bob手......
  • 23~24 炼石计划 NOIP 练习题部分题解
    目录目录第1组JOISC2017火车旅行IOI2018会议CF1558FStrangeSortAPIO2018新家CTSC2017密钥CF1748EYetAnotherArrayCountingProblem第2组NOI2016区间LOJ552MIN&MAXIJOISC2023合唱LOJ542序列划分LOJ560Menci的序列P8978中位数第3组USACO20FEBHelpYourse......
  • CF35C. Fire Again 题解 bfs求最短路
    题目链接:https://codeforces.com/problemset/problem/35/C视频讲解:https://www.bilibili.com/video/BV1tZ1FYPELp/?p=4以每个着火的点为起点求最短路,然后输出任意一个距离值最大的点即可。需要注意的是:本题是文件输入输出。示例程序:#include<bits/stdc++.h>usingnamespace......
  • CF1139C. Edgy Trees 题解 并查集
    题目链接:https://codeforces.com/problemset/problem/1139/C视频讲解:https://www.bilibili.com/video/BV1tZ1FYPELp?p=3我们可以求总方案数-不满足条件的方案数。设一个不包含黑色边的极大连通块的大小为\(sz_i\)。则答案为\[n^k-\sum\{sz_i^k\}\]示例程序:#include......
  • CF1800E2. Unforgivable Curse (hard version) 题解 并查集
    题目链接:https://codeforces.com/contest/1800/problem/E2视频讲解:https://www.bilibili.com/video/BV1tZ1FYPELp?p=2把下标\(i\)对应到图中编号为\(i\)的节点。节点\(i\)和\(i+k\)之间连一条边,节点\(i\)和\(i+k+1\)之间也连一条边。同一个连通块里的节点对应的字......
  • P5661 [CSP-J2019] 公交换乘 题解
    模拟"公交换乘"按题意模拟即可.注意:可以使用结构体,但是超过时间的优惠券需要被忽略.代码#include<iostream>#include<cstdio>usingnamespacestd;structnode{ intprice,deadline,is_use;//价格,截止时间,是否使用过}a[100005];intn,p,ans,pos=1;int......
  • P5662 [CSP-J2019] 纪念品 题解
    背包因为小伟可以每天进行$2$种操作无限次,所以显然可以使用完全背包.定义状态$f_i$,表示还剩下$i$时,可以拿到钱的最大值.再假设小伟今天买了,明天就卖掉.状态转移方程如下:$f_i=max(f_i,f_{i-p_{k,i}}+p_{k+1,i}-p_{k,i}).$即今天花掉的钱+明天能拿的钱-今天花掉的......
  • P5663 [CSP-J2019] 加工零件 题解
    最短路对于上图,如果我们相知道$2$号工人想要一个$3$阶段的零件,其实是看$2$到$1$有没有一条长度为$3$的路径.但如果要求$4$阶段的路径,那就不一定了.所以我们直接求一遍最短路,分奇最短路和偶最短路.处理完后,最后一次$\Theta(1)$的回答,如果路径长度过大,就是$No$,......
  • Nginx的 MIME TYPE问题导致的mjs文件加载出错的问题解决
    .mjs文件:明确表示使用ES6模块系统(ECMAScriptModules)。 在服务器用Nginx部署前端项目后,出现下面这种问题Failedtoloadmodulescript:ExpectedaJavaScriptmodulescriptbuttheserverrespondedwithaMIMEtypeof"application/octet-stream".StrictMIMEt......