HDU 4777 Rabbit Kingdom【树状数组】

题意:

给定序列,若干询问,求给定询问区间中互质的数的个数。

分析:

智商太低理解了好久好久,最后还是看别人讲解和代码明白的。参考
l[i] 为位置为 i 的元素左边第一个与w[i]不互质的数, r[i] 为右边第一个与 w[i] 不互质的数,那么 (l[i],r[i]) 区间内的所有数均与 w[i] 互质。
假设我们从头开始扫,为了防止重复计算,我们不管 i 左边的数,那么每遇到i,就把 i 的贡献加到后面相应的区间[i,r[i])里,实现起来我们就可以用树状数组(或线段树)维护区间互质数的对数和,每遇到 i ,就add(i,1),add(r[i],1)
下面处理询问,首先对所有询问根据区间左端点进行排序,然后我们从头开始扫,设头指针 pp=1 ,每次遇到 l[i]=pp 时,说明对于左端点在 pp 之后的询问他是有贡献的,就把他的贡献加到相应的区间中,即 [i,r[i]) ,指针扫过时再进行还原。我们可以用 vector 保存每个 l[i]=pp 的元素位置 i ,然后每次访问到一个位置直接遍历相应的vector即可。

代码:

#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 200000 + 5, oo = 0x3f3f3f3f;
vector<int>prime[maxn];
vector<int>loc[maxn];
int l[maxn], r[maxn];
int w[maxn];
int pos[maxn];
int bit[maxn];
int ans[maxn];
bool isprime[maxn];
int n, q;
struct Q{
    int lq; int rq;int num;
    bool operator < (const Q &a) const{
        return lq < a.lq;
    }
};
Q query[maxn];
int h[maxn];
void initprime()
{
    int tot = 0;
    memset(isprime, false, sizeof(isprime));
    memset(h, 0, sizeof(h));
    for(int i = 2; i < maxn; i++){
        if(!isprime[i]){
            h[tot++] = i;
            for(int j = 2 * i; j < maxn; j += i){
                isprime[j] = true;
            }
        }
   }
   for(int i = 0; i < tot; i++){
        for(int j = h[i]; j < maxn; j += h[i]){
            prime[j].push_back(h[i]);
        }
   }
}
void add(int i, int x)
{
    while(i <= n){
        bit[i] += x;
        i += i & (-i);
    }
}
int sum(int i)
{
    int ans = 0;
    while(i){
        ans += bit[i];
        i -= i & (-i);
    }
    return ans;
}
int main (void)
{
    initprime();
    while(scanf("%d%d", &n, &q) && (n + q)){
        for(int i = 1; i <= maxn; i++){
            loc[i].clear();
        }
        for(int i = 1; i <= n; i++){
            scanf("%d", &w[i]);
        }
        memset(pos, 0, sizeof(pos));
        int a;
        for(int i = 1; i <= n; i++){
            a = 0;
            for(int j = 0; j < prime[w[i]].size(); j++){
                a = max(a, pos[prime[w[i]][j]]);
                pos[prime[w[i]][j]] = i;
            }
            l[i] = a;
            loc[a].push_back(i);
        }
        a = oo;
        memset(pos, 0x3f, sizeof(pos));
        for(int i = n; i >= 1; i--){
            a = oo;
            for(int j = 0; j <  prime[w[i]].size(); j++){
                a = min(a, pos[prime[w[i]][j]]);
                pos[prime[w[i]][j]] = i;
            }
            r[i] = a;
        }
        //for(int i = 1; i <= n; i++)cout<<i<<' '<<l[i]<<' '<<r[i]<<endl;
       int tl, tr;
       for(int i = 0; i < q; i++){
            scanf("%d%d", &query[i].lq, &query[i].rq);
            query[i].num = i;
       }
       sort(query, query + q);
       memset(bit, 0, sizeof(bit));
       for(int i = 1; i <= n; i++){
            if(!l[i]){
                add(i, 1);
                if(r[i] <= n) add(r[i], -1);
            }
       }
       int pp = 1;
       for(int i = 0; i < q; i++){
            while(pp < query[i].lq){
                add(pp, -1);
                if(r[pp] <= n) add(r[pp], 1);
                for(int j = 0; j < loc[pp].size(); j++){
                    add(loc[pp][j], 1);
                    if(r[loc[pp][j]] <= n) add(r[loc[pp][j]], -1);
                }
                pp++;
            }
            ans[query[i].num] = sum(query[i].rq) - sum(query[i].lq - 1);
        }
        for(int i = 0; i < q; i++){
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值