题解 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;
}
全部评论

相关推荐

家里人这种思想对吗?最近找到了某大厂算法岗的实习,家里人一直跟我说要给领导买点东西,搞好关系,我真的搞不清楚他们这种思想到底怎么来的,真的很烦他们教我做事,他们总觉得自己是对的,我不照着他们的想法做,就觉得我态度不对,之前找实习也是只会嘴巴上对我说你要加油,你要努力,但是根本不知道我背后付出了多少努力,真的好烦被教做事的感觉。
青春运维少年不会梦到...:小时候老爸每次外出打工,我都会说注意安全,可是我真的懂老爸的工作吗,一个小学文凭的人出去打工能有什么安全的工作,可是老爸还是慈祥的回应我,仿佛每天能安全回家都归功于我的祈福。到了现在,我跨越3000多公里去了陌生的城市,老爸还是那个老爸,只不过现在多了问我的情况,会问我适应新城市吗,适应工作强度吗,到最后真的好奇,问我这个工作是干啥的;老爸没文化,不知道计算机网络有七层结构,也不知道云saas订阅,我只能说,就像汽车修理厂一样,我是那个修车的师傅。老爸可能觉得真的理解不了我的工作,之后也就没多问了。不过仍然还是给我传授他的经验,对于老爸来说,他也知道我做的是他难以理解的工作,知道小县城的那套江湖规矩难以闯荡大城市,但是他依旧会关心我。。。
实习的内耗时刻
点赞 评论 收藏
分享
07-12 20:55
武汉大学 Java
程序员小白条:熟悉 Java、Python、Go 全能选手,这实习还是线上2个月,呃呃呃,没啥用,整个简历写的很差,也就是9爷的学历了
点赞 评论 收藏
分享
头像
08-12 10:14
门头沟学院 Java
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务