1. 程式人生 > >hdu 4777 Rabbit Kingdom (樹狀陣列+離線處理)

hdu 4777 Rabbit Kingdom (樹狀陣列+離線處理)

題目連結:哆啦A夢傳送門

題意:給一串n個數字,m個詢問,每次詢問的區間中,與其他元素都互素的數字有多少個?

 

參考連結:https://www.cnblogs.com/kuangbin/p/3416181.html

https://www.cnblogs.com/shuguangzw/p/5272595.html

題解:這又是讓你找互素,老辦法,每次讓你找有關互素的,你都不要去找,而去找不互素的,因為不互素我們可以通過質因子分解來解決。這題也是。

我們先每個數預處理出L,R區間,表示左右和這個數不互質的位置。這個只要從左到右和從右到左掃描一遍,分解質因素,找下一個質因素的位置。

然後對於每個查詢進行離線處理,按照右端點排序

更新的過程是這樣的

       (1)對於剛加入點x,樹狀陣列L[x]位置+1   把這個定義為左更新

       (2)對於所有R[i]=x的點,樹狀陣列L[i]位置-1,i位置+1   把這個定義為右更新

       (3)查詢是詢問區間 l->r的和

 

至於為什麼要這樣更新,這裡題解用的方法確實很妙,實在很難簡單用三言兩語將清楚,我建議模擬一遍,不過程式碼裡還是寫了點註釋。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;

const int maxn=200010;

int prime[maxn],tot;
bool book[maxn];

void getprime()
{
    memset(book,1,sizeof(book));
    book[1]=tot=0;

    for(int i=2;i<=200000;i++)
    {
        if(book[i]) prime[++tot]=i;
        for(int j=1;j<=tot&&i*prime[j]<=200000;j++)
        {
            book[i*prime[j]]=0;
            if(i%prime[j]==0) break;
        }
    }
}

int factor[100][2];
int fatcnt;
void getfactor(int x)
{
    fatcnt=0;
    int tmp=x;
    for(int i=1;prime[i]<=tmp/prime[i];i++)
    {
        factor[fatcnt][1]=0;
        if(tmp%prime[i]==0)
        {
            factor[fatcnt][0]=prime[i]; ///儲存素因子
            while(tmp%prime[i]==0){
                factor[fatcnt][1]++; ///儲存素因子個數
                tmp/=prime[i];
            }
            fatcnt++;
        }

    }
    if(tmp!=1){
        factor[fatcnt][0]=tmp;
        factor[fatcnt++][1]=1;
    }
//    return fatcnt;
}

int L[maxn],R[maxn];
int a[maxn];
int b[maxn];
int n,m;
int c[maxn];

void add(int i,int val)
{
    if(i==0) return;
    while(i<=n){
        c[i]+=val;
        i=i+(i&(-i));
    }
}

int sum(int i)
{
    int sum=0;
    while(i>0)
    {
        sum+=c[i];
        i-=(i&(-i));
    }
    return sum;
}

vector<int> vec[maxn];
struct node{
    int l,r,index;
}num[maxn];

bool cmp(node x,node y){ return x.r<y.r;}

int ans[maxn],pp[maxn][15];

int main()
{

    getprime();
//    for(int i=1;i<=10;i++)
//        printf("%d ",prime[i]);
//    puts("");

    while(scanf("%d%d",&n,&m))
    {
        if(m==0&&n==0) break;

        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);

        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&num[i].l,&num[i].r);
            num[i].index=i;
        }

        for(int i=1;i<maxn;i++) b[i]=n+1;

        for(int i=n;i>=1;i--)
        {

            getfactor(a[i]);///得到a[i]的質因子
            R[i]=n+1; ///假設為最右端+1
            pp[i][0]=fatcnt; ///儲存a[i]的素因子個數
            for(int j=0;j<fatcnt;j++)
            {
                R[i]=min(R[i],b[factor[j][0]]);
                b[factor[j][0]]=i;///標記質因子的位置
                pp[i][j+1]=factor[j][0];///儲存a[i]的素因子
            }
        }

        for(int i=1;i<maxn;i++) b[i]=0;

        for(int i=1;i<=n;i++)
        {
            L[i]=0; ///假設為最左端減1
            fatcnt=pp[i][0];
            for(int j=0;j<fatcnt;j++)
            {
                int item=pp[i][j+1];
                L[i]=max(L[i],b[item]);
                b[item]=i;
            }
        }
//         for(int i=1;i<=n;i++)
//            printf("L[%d]=%d ",i,L[i]);
//        puts("");
//        for(int i=1;i<=n;i++)
//            printf("R[%d]=%d ",i,R[i]);
//        puts("");

        sort(num+1,num+1+m,cmp);
        memset(c,0,sizeof(c));

        for(int i=1;i<=n;i++){
            c[i]=0;
            vec[i].clear();
        }

        for(int i=1;i<=n;i++)
            vec[R[i]].push_back(i); ///儲存以R[i]為右端的點集

        int id=1;

        for(int i=1;i<=m;i++)
        {
            while(id<=n&&id<=num[i].r)
            {
                ///我們在L[id]的位置加1,那麼說明在當前以num[i].r為右端點的區間
                ///不互質數加1
                add(L[id],1);
                int SIZE=vec[id].size();

                for(int j=0;j<SIZE;j++){///說明有以R[id]為界的點
                    int v=vec[id][j];
                    add(v,1);///我們在v位置加1,因為某個區間要查詢到v,故要在這加1
                    add(L[v],-1);///我們在L[v]位置減1,因為前面已經加了1,故在這要減1
                    
                }
                id++;
            }
            int len=sum(num[i].r)-sum(num[i].l-1);
            ans[num[i].index]=num[i].r-num[i].l+1-len;
        }

        for(int i=1;i<=m;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}