题目描述:
有一个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+1−bj,gcd(bj+2−bj+1,…,bk−bk−1)))。那么可以发现除去第一项外,都是相邻列的差值。再考虑所有的行之间的 gcd 。gcd(u1,u2,…,uk) 由于每一个ui内都有 gcd(bj+1−bj,…bk−bk−1) ,所以可以将 gcd(u1,u2,…uk) 转化为 gcd(ai+bj,ai+1+bj,…,ak+bj,gcd(bj+1−bj,…,bk−bk−1)) 再次根据 gcd 的性质,将上述式子变成 gcd(ai+bj,gcd(ai+1−ai,ai+2−ai+1…ak−ak−1),gcd(bj+1−bj,…bk−bk−1))。
所以我们要做的就是求出区间的 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 表来实现
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现