HDU4777 Rabbit Kingdom 树状数组

题目大意

题目链接

杭州现场赛的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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值