hdu 4777 Rabbit Kingdom (树状数组+离线处理)

版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84947995

题目链接:哆啦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;
}

猜你喜欢

转载自blog.csdn.net/LJD201724114126/article/details/84947995
今日推荐