题目大意:
有一个长度为 n n n 的整数序列,有 q q q 次询问,每次询问给出一个区间 [ l , r ] [l,r] [l,r] 求出所有不能被区间所有数整除的数量。
题解:
考虑询问区间中能被区间所有数的数量。
首先这类数一定是询问区间中最小的数,且必定为这段区间最大公因数的因数,但又因为这类数是区间最小值,且区间 gcd \gcd gcd 必定小于等于区间最小值,故这类数一定为区间 gcd \gcd gcd。
所以我们可以用线段树统计出区间最小值,区间最小值个数,以及区间 gcd \gcd gcd.
在每次询问统计答案时,若区间最小值为区间 gcd \gcd gcd,则答案累加区间最小值最小值的个数即可。
线段树每次查询以及建树都是 O ( log n ) O(\log n) O(logn) 的,所以总复杂度为 O ( q log n ) O(q \log n) O(qlogn).
参考代码:
#include<bits/stdc++.h>
using namespace std;
int read() {
int f = 1, x = 0; char ch = getchar();
while((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if(ch == '-') f = -1, ch = getchar();
while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x * f;
}
const int N = 1e5 + 10;
struct Segment {
int g, cnt, minn;
}t[N << 2];
int n, q, a[N];
void pushup(int p) { //更新节点的值
t[p].minn = min(t[p << 1].minn, t[p << 1 | 1].minn);
t[p].g = __gcd(t[p << 1].g, t[p << 1 | 1].g);
if(t[p << 1].minn == t[p << 1 | 1].minn) t[p].cnt = t[p << 1].cnt + t[p << 1 | 1].cnt;
else if(t[p << 1].minn < t[p << 1 | 1].minn) t[p].cnt = t[p << 1].cnt;
else if(t[p << 1 | 1].minn < t[p << 1].minn) t[p].cnt = t[p << 1 | 1].cnt;
}
void build(int p, int l, int r) { //建树
int mid = l + r >> 1;
if(l == r) {
t[p].cnt = 1;
t[p].g = a[l];
t[p].minn = a[l];
}
else {
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
}
Segment query(int p, int l, int r, int x, int y) { // 查询
if(l > y || r < x) return (Segment){0, 0, 1000000000};
if(x <= l && r <= y) return t[p];
int mid = l + r >> 1;
Segment lc = query(p << 1, l, mid, x, y);
Segment rc = query(p << 1 | 1, mid + 1, r, x, y);
Segment tmp;
tmp.minn = min(lc.minn, rc.minn);
tmp.g = __gcd(lc.g, rc.g);
if(lc.minn == rc.minn) tmp.cnt = lc.cnt + rc.cnt;
else if(lc.minn < rc.minn) tmp.cnt = lc.cnt;
else if(rc.minn < lc.minn) tmp.cnt = rc.cnt;
return tmp;
}
int main() {
n = read();
for(int i = 1; i <= n; ++i) a[i] = read();
build(1, 1, n);
q = read();
while(q --) {
int l, r;
l = read(), r = read();
Segment answer = query(1, 1, n, l, r);
int ans = 0;
if(answer.minn == answer.g) ans = answer.cnt; //处理答案
cout << r - l + 1 - ans << endl;
}
return 0;
}