题解 Matrix and GCD

Matrix and GCD

https://ac.nowcoder.com/acm/contest/33194/F

本题解仅提供F题的另一种求解全一子矩阵个数的方法

众所周知, 求解全一子矩阵的问题可以用 单调栈 解决, 例如最大子矩阵, 子矩阵个数

而有另一种工具也可以有效而优雅地解决该类问题, 那便是 笛卡尔树

(上面的超链接里有介绍笛卡尔树,并且提供了求解 最大子矩阵 的做法和例题)

如果你熟悉了 笛卡尔树求解最大子矩阵 的做法,相信 求解子矩阵的数量 也很好理解。

假设以当前为底列连续全1列的高分别为 {}

那么对这k个数字建立小根堆笛卡尔树之后,对于当前分治中心u,区间假设为

计算经过u这一点的区间,满足

那么区间数量即为

因为是小根堆,所以目前任何区间 ,都可以向上延展最多至

从而可以得出 经过当前分治中心u的矩阵数量为 =

// 分治代码
long long sum;
int dfs(int u) {
  if (!u) return 0;
  int L = dfs(t[u].ls) + 1;
  int R = dfs(t[u].rs) + 1;
  sum += 1LL * L * R * t[u].w;
  return L + R - 1;
}
// F题完整代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
const int M = 1000005;
#define For(i,j,k) for(int i=(int)(j);i<=(int)(k);i++)
namespace Sieve {
  int cnt[M];
  void sieve(int n) {
    iota(cnt, cnt + n + 1, 0);
    for (int i = 1; i <= n; i++)
      for (int j = i << 1; j <= n; j += i)
        cnt[j] -= cnt[i];
  }
}
struct Node { int w, f, ls, rs; } t[N];
int build(int n) { // 建树
  for (int i = 1; i <= n; i++) {
    int k = i - 1;
    while (t[k].w > t[i].w) k = t[k].f;
    t[i].ls = t[k].rs, t[t[i].ls].f = i;
    t[i].f = k, t[k].rs = i;
  }
  return t[0].rs;
}
long long sum;
int dfs(int u) { // 分治
  if (!u) return 0;
  int L = dfs(t[u].ls) + 1;
  int R = dfs(t[u].rs) + 1;
  sum += 1LL * L * R * t[u].w;
  return L + R - 1;
}
int n, m, k, a[N][N];
short X[M], Y[M];
short f[N][N];
long long solve(int s) {
  sum = 0;
  for (int i = s; i <= n * m; i += s) {
    f[X[i]][Y[i]] = -1;
  }
  short x, y, cur;
  for (int i = s; i <= n * m; i += s) {
    x = X[i], y = Y[i], cur = 0;
    if (f[x][y] == -1 && f[x-1][y] == 0)
      while (f[x][y] == -1) f[x][y] = ++cur, ++x;
  }
  for (int i = s; i <= n * m; i += s) {
    x = X[i], y = Y[i];
    if (f[x][y] > 0 && f[x][y-1] == 0) {
      t[k = 0] = { 0, 0, 0, 0 };
      for (int j = y; f[x][j] > 0; j++) {
        t[++k] = { f[x][j], 0, 0, 0 };
        f[x][j] = 0;
      }
      dfs(build(k));
    }
  }
  return sum;
}
int main() {
  ios_base::sync_with_stdio(false);
  cin >> n >> m;
  Sieve::sieve(n * m);
  For(i, 1, n) For(j, 1, m)
    cin >> k, X[k] = i, Y[k] = j;
  long long res = 0;
  For(i, 1, n * m) res += solve(i) * Sieve::cnt[i];
  cout << res;
  return 0;
}
全部评论

相关推荐

11-24 00:11
已编辑
广东工业大学 算法工程师
避雷深圳&nbsp;&nbsp;yidao,试用期&nbsp;6&nbsp;个月。好嘛,试用期还没结束,就直接告诉你尽快找下一家吧,我谢谢您嘞
牛客75408465号:笑死,直属领导和 hr 口径都没统一,各自说了一些离谱的被裁理由,你们能不能认真一点呀,哈哈哈哈哈😅😅😅
点赞 评论 收藏
分享
牛客737698141号:他们可以看到在线简历的。。。估计不合适直接就拒了
点赞 评论 收藏
分享
爱看电影的杨桃allin春招:我感觉你在炫耀
点赞 评论 收藏
分享
评论
1
1
分享
牛客网
牛客企业服务