ABC254 F Rectangle GCD(数学 + 线段树)

F - Rectangle GCD

题目描述:

有一个N×N的平面,有两个序列Ai,Bi,平面上的点(i,j)对应的值是Ai+Bj,有Q次询问,每次询问要查询 (h1,w2)(h2,w2) 为左上和右下顶点的矩形内所有数的最大公约数是多少

思路:

  首先考虑每一行所有数的最大公约数 gcd(ai+bj,gcd(ai+bj+1,,gcd(ai+bk))),根据gcd的性质可以将上述式子转化成 ui=gcd(ai+bj,gcd(bj+1bj,gcd(bj+2bj+1,,bkbk1)))。那么可以发现除去第一项外,都是相邻列的差值。再考虑所有的行之间的 gcdgcd(u1,u2,,uk) 由于每一个ui内都有 gcd(bj+1bj,bkbk1) ,所以可以将 gcd(u1,u2,uk) 转化为 gcd(ai+bj,ai+1+bj,,ak+bj,gcd(bj+1bj,,bkbk1)) 再次根据 gcd 的性质,将上述式子变成 gcd(ai+bj,gcd(ai+1ai,ai+2ai+1akak1),gcd(bj+1bj,bkbk1))
  所以我们要做的就是求出区间的 gcd, 而能够维护区间 gcd 的有线段树和 ST 表。用这俩维护一下行和列差值的区间 gcd 的就可以了。

struct Seg { const int n; std::vector<int> val; Seg(int n) : n(n), val(4 << std::__lg(n)) { std::function<void(int, int, int)> build = [&](int u, int l, int r) -> void { if (l == r) return ; int mid = l + r >> 1; build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); val[u] = std::__gcd(val[u << 1], val[u << 1 | 1]); }; build(1, 1, n); } void modify(int u, int l, int r, int pos, int x) { if (l == r) return void(val[u] = x); int mid = l + r >> 1; if (mid >= pos) modify(u << 1, l, mid, pos, x); else modify(u << 1 | 1, mid + 1, r, pos, x); val[u] = std::__gcd(val[u << 1], val[u << 1 | 1]); } int query(int u, int l, int r, int ln, int rn) { if (l >= ln && r <= rn) return val[u]; int mid = l + r >> 1; int ans = 0; if (mid >= ln) ans = std::__gcd(ans, query(u << 1, l, mid, ln, rn)); if (mid < rn) ans = std::__gcd(ans, query(u << 1 | 1, mid + 1, r, ln, rn)); return ans; } }; signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n, q; std::cin >> n >> q; Seg SGT1(n + 1), SGT2(n + 1); std::vector<int> a(n + 1), b(n + 1); for (int i = 1; i <= n; i++) { std::cin >> a[i]; } for (int i = 1; i <= n; i++) { std::cin >> b[i]; } for (int i = 1; i < n; i++) { SGT1.modify(1, 1, n - 1, i, std::abs(a[i + 1] - a[i])); SGT2.modify(1, 1, n - 1, i, std::abs(b[i + 1] - b[i])); } for (int i = 0; i < q; i++) { int h1, h2, w1, w2; std::cin >> h1 >> h2 >> w1 >> w2; int res = a[h1] + b[w1]; h2--, w2--; int ret = 0; ret = std::__gcd(h1 <= h2 ? SGT1.query(1, 1, n - 1, h1, h2) : 0, w1 <= w2 ? SGT2.query(1, 1, n - 1, w1, w2) : 0); std::cout << std::__gcd(res, ret) << "\n"; } return 0 ^ 0; }

由于这个不存在任何的修改操作,所以也可以选择常数更小更好写的 ST 表来实现

int ga[20][200010], gb[20][200010]; int geta(int l, int r) { if (l > r) return 0; int k = std::__lg(r - l + 1); return std::__gcd(ga[k][l], ga[k][r - (1 << k) + 1]); } int getb(int l, int r) { if (l > r) return 0; int k = std::__lg(r - l + 1); return std::__gcd(gb[k][l], gb[k][r - (1 << k) + 1]); } signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n, q; std::cin >> n >> q; std::vector<int> a(n + 1), b(n + 1); for (int i = 1; i <= n; i++) { std::cin >> a[i]; } for (int i = 1; i <= n; i++) { std::cin >> b[i]; } int len = std::__lg(n) + 1; for (int i = 1; i < n; i++) { ga[0][i] = std::abs(a[i + 1] - a[i]); gb[0][i] = std::abs(b[i + 1] - b[i]); } for (int i = 1; i < len; i++) { for (int j = 1; j <= n - (1 << i) + 1; j++) { ga[i][j] = std::__gcd(ga[i - 1][j], ga[i - 1][j + (1 << (i - 1))]); gb[i][j] = std::__gcd(gb[i - 1][j], gb[i - 1][j + (1 << (i - 1))]); } } for (int i = 0; i < q; i++) { int h1, h2, w1, w2; std::cin >> h1 >> h2 >> w1 >> w2; int res = a[h1] + b[w1]; int ret = 0; ret = std::__gcd(geta(h1, h2 - 1), getb(w1, w2 - 1)); std::cout << std::__gcd(res, ret) << "\n"; } return 0 ^ 0; }

__EOF__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/16740711.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示