题目链接: Bash and a Tough Math Puzzle
大致题意
给定一个长度为n的序列, 有两种操作:
1 l r x
询问[l, r]区间, 最多修改1个位置的数字的条件下, 能否使得区间内所有数字的gcd = x.
2 a c
把a位置的数字修改为c.
解题思路
思维
我们先考虑查询操作. 设区间[l, r]gcd的结果为 d d d, 如果d是x的倍数, 则我们一定可以至多修改一个位置, 使得d’ = x. 那这又代表着什么? 这代表着, [l, r]每一个位置上的数字都是x的倍数.
如果d不是x的倍数, 则表明区间内一定存在至少一个位置上的数字不是x的倍数. 我们只需要修改不是x的倍数的位置即可.
线段树
我们考虑用线段树来维护区间gcd的信息.
每次查询时, 我们相当于询问这个区间不是x的倍数的数字有几个. 这里注意剪枝.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E5 + 10;
int w[N];
struct node {
int l, r;
int val;
}t[N << 2];
void pushup(int x) { t[x].val = gcd(t[x << 1].val, t[x << 1 | 1].val); }
void build(int l, int r, int x = 1) {
t[x] = { l, r, w[l] };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}
void modify(int a, int c, int x = 1) {
if (t[x].l == t[x].r) {
t[x].val = c;
return;
}
int mid = t[x].l + t[x].r >> 1;
modify(a, c, x << 1 | (a > mid));
pushup(x);
}
int num;
void fact(int l, int r, int c, int x = 1) {
if (num > 1 or t[x].val % c == 0) return;
if (t[x].l == t[x].r) {
num += t[x].val % c != 0;
return;
}
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) fact(l, r, c, x << 1);
if (r > mid) fact(l, r, c, x << 1 | 1);
}
int main()
{
int n; cin >> n;
rep(i, n) scanf("%d", &w[i]);
build(1, n);
int m; cin >> m;
rep(i, m) {
int tp; scanf("%d", &tp);
if (tp == 1) {
int l, r, c; scanf("%d %d %d", &l, &r, &c);
num = 0;
fact(l, r, c);
puts(num <= 1 ? "YES" : "NO");
}
else {
int a, c; scanf("%d %d", &a, &c);
modify(a, c);
}
}
return 0;
}