HDU4777 Rabbit Kingdom(樹狀數組)

傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=4777
題意:n個數,m個詢問,每個詢問是一個[L,R]區間,問你這個區間內,有幾個數字與其他的數字互質。
思路:想了很久還是不會做,看了一些別人的博客,算是有點明白了。先求出2-200000裏每個數字的素數因子(包括本身),然後用一個數組標記素數因子的位置,從左到右掃一遍,從右到左掃一遍,求出L[]和R[],表示每個位置的左邊第一個與他不互質的數的位置,和右邊第一個與他不互質的數的位置。求出L和R以後,就可以用樹狀數組來操作。我們可以知道一個特點,對於一個區間[L,R],我們只要用樹狀數組在L點+1,在R+1處-1,就可以使得[L,R]區間整體+1。我們通過L數組和R數組,可以知道,當前這個位置所起到與所有數字互質的作用範圍是[L+1,R-1],但是我們要用求得答案的時候是要sum(R)-sum(L-1),所以我們直接用樹狀數組來操作[x(當前位置),R]區間就可以了。那麼利用這個思路,我們把查詢離線,按照查詢的L從小到大排序。通過一個變量來表示當前掃描的位置,然後從左到右掃描同時遍歷詢問,我們用每一個左區間的位置來存放數字的位置,保存下一個表,那就能可以通過當前這個左區間得知哪些位置使用了這個左區間,然後掃描的時候就可以利用這個表來更新樹狀數組。
對於每一個詢問,答案是sum(R)-sum(L-1)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define mp make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1|1
#define calm (l + r) >> 1
const int INF = 1e9+7;

const int N=200010;
struct P{
    int l,r,id;
}input[N];
int n,m,a[N],tree[N],l[N],r[N],p[N],ans[N];
bool vis[N];
vector<int> fac[N],pos[N];

void init(){//預處理每個數字的素因子,包含本身
    for(int i=2;i<=200000;i++){
        if(!vis[i]){
            fac[i].pb(i);
            for(int j=i+i;j<=200000;j+=i){
                vis[j]=true;
                fac[j].pb(i);
            }
        }
    }
}
int sum(int x){
    int ans=0;
    while(x){
        ans+=tree[x];
        x-=x&-x;
    }
    return ans;
}
void add(int x,int v){
    while(x<=n){
        tree[x]+=v;
        x+=x&-x;
    }
}
inline bool cmp(P a,P b){
    return a.l<b.l;
}
int main(){
    freopen("D://input.txt","r",stdin);
    init();
    while(scanf("%d%d",&n,&m)!=EOF&&n&&m){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            pos[i].clear();
        }
        for(int i=0;i<m;i++){
            scanf("%d%d",&input[i].l,&input[i].r);
            input[i].id=i;
        }
        memset(tree,0,sizeof tree);
        memset(p,0,sizeof p);
        for(int i=1;i<=n;i++){//cal l[i]
            int now=0;
            for(int j=0,len=fac[a[i]].size();j<len;++j){
                now=max(now,p[fac[a[i]][j]]);
                p[fac[a[i]][j]]=i;
            }
            l[i]=now;
            pos[now].pb(i);//在每個左區間,記錄使用這個左區間的數字,便於掃描
        }
        memset(p,127,sizeof p);
        for(int i=n;i>=1;i--){//cal r[i]
            int now=n+1;
            for(int j=0,len=fac[a[i]].size();j<len;++j){
                now=min(now,p[fac[a[i]][j]]);
                p[fac[a[i]][j]]=i;
            }
            r[i]=now;
        }
        sort(input,input+m,cmp);
        for(int i=1;i<=n;i++){
            if(l[i]<1){
                add(i,1);
                if(r[i]<=n)add(r[i],-1);
            }
        }
        int cur=1;//作爲掃描的位置標誌
        for(int i=0;i<m;i++){
            while(cur<input[i].l){
                add(cur,-1);//先把事先加上去的減去
                if(r[cur]<=n)add(r[cur],1);
                for(int j=0,len=pos[cur].size();j<len;++j){//遍歷使用了這個左區間的數字的位置,給他們的區間加上他們這個位置的貢獻
                    add(pos[cur][j],1);
                    if(r[pos[cur][j]]<=n)add(r[pos[cur][j]],-1);
                }
                ++cur;
            }
            ans[input[i].id]=sum(input[i].r)-sum(input[i].l-1);
        }
        for(int i=0;i<m;i++){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章