题目大意
杭州现场赛的H题……
题意是给定一个长度为N的序列,然后有M个询问,每个询问是询问L到R内有多少个数和其他所有的数都互质
解题思路
首先,对于这个序列中的每一个数,我们都可以求出来在这个序列中与它相邻且互质的连续区间,然后离线处理一下询问
具体的处理方法就是,求出来所有的数的所有的互质范围有多少个包含当前询问的区间,减去所有数的前半范围到当前位置i-1的数有多少个包含这个区间,以及从当前位置i到后半范围有多少个当前询问区间,就是当前询问的答案。
其实在纸上仔仔细细地画一遍这个区间就知道啦……处理出来所有数的互质范围包含了多少个当前询问区间的时候,这肯定是多了的。。在这个时候,我们一定要减去那些多询问了的地方,那么,如果当前数的互质区间的左范围小于等于当前询问区间的左范围,那么我们把它的左半区间给维护上,那么如果这个左半区间完整包含了当前询问区间,那么这个数不就在当前询问区间的右边了么,然后如果某个数的右半区间完整包含了当前询问区间,那么就说明这个数在询问区间的左边,换句话说,其实我们的目的是处理掉不在当前询问区间里面,但是互质区间还包含了当前询问区间的数的个数,但是由于我们要离线处理,所以就这样了……
最初写的时候错了一次,因为我在询问的时候询问的不是完整覆盖询问区间,结果导致了覆盖一部分的也被我维护上并且一顿乱减……
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = (int)2e5 +10;
int p[N];
struct co_prime{
int l, r, z;
}t[N];
struct query{
int l, r, num;
}q[N];
int s1[N], s2[N], s3[N], c[N], d[N], a[N], n, m;
void prime(){
memset(p, 0, sizeof(p));
p[1] = 1;
for (int i = 2; i < N; i++) if (p[i] == 0)
for (int j = i; j < N; j += i) p[j] = i;
}
bool cmpq(query a, query b){
return a.l < b.l;
}
bool cmp1(co_prime a, co_prime b){
return a.l < b.l;
}
bool cmp2(co_prime a, co_prime b){
return a.z < b.z;
}
int lowbit(int x){return x & (-x);}
void update(int st, int val){
for (int i = st; i <= n; i += lowbit(i)) c[i] += val;
}
int getsum(int w){
int s = 0;
for (int i = w; i >= 1; i -= lowbit(i)) s += c[i];
return s;
}
void solve(){
memset(s1, 0, sizeof(s1));
memset(s2, 0, sizeof(s2));
memset(s3, 0, sizeof(s3));
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= m; i++){
scanf("%d%d", &q[i].l, &q[i].r);
q[i].num = i;
}
memset(d, 0, sizeof(d));
for (int i = 1; i <= n; i++){
int mm = 1; t[i].z = i;
for (int j = a[i]; j != 1; j /= p[j]){
if (d[p[j]] + 1 > mm && d[p[j]] != i) mm = d[p[j]] + 1;
d[p[j]] = i;
}
t[i].l = mm;
}
for (int i = 0; i <= N - 10; i++) d[i] = n + 1;
for (int i = n; i >= 1; i--){
int mm = n;
for (int j = a[i]; j != 1; j /= p[j]){
if (d[p[j]] - 1 < mm && d[p[j]] != i) mm = d[p[j]] - 1;
d[p[j]] = i;
}
t[i].r = mm;
}
memset(c, 0, sizeof(c));
sort(q + 1, q + m +1, cmpq);
sort(t + 1, t + 1 + n, cmp1);
int w = 1;
for (int i = 1; i<= m; i++){
for (; w <= n; w++){
if (t[w].l > q[i].l) break;
update(t[w].l, 1);
update(t[w].r + 1, -1);
}
s1[q[i].num] = getsum(q[i].r);
}
memset(c, 0, sizeof(c));
w = 1;
for (int i = 1; i <= m; i++){
for (; w <= n; w++){
if (t[w].l > q[i].l) break;
update(t[w].l, 1);
update(t[w].z, -1);
}
s2[q[i].num] = getsum(q[i].r);
}
memset(c, 0, sizeof(c));
w = 1;
sort(t + 1, t + 1 + n, cmp2);
for (int i = 1; i <= m; i++){
for (; w <= n; w++){
if (t[w].z >= q[i].l) break;
update(t[w].z, 1);
update(t[w].r + 1, -1);
}
s3[q[i].num] = getsum(q[i].r);
}
for (int i = 1; i <= m; i++) printf("%d\n", s1[i] - s2[i] - s3[i]);
}
int main(){
prime();
while(scanf("%d%d", &n, &m) == 2 && (n + m)) solve();
return 0;
}