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

273 篇文章 0 订阅
112 篇文章 0 订阅

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

分析:对每个数预处理,求出与a[i]不互素的左右两边最远的位置l[i]和r[i],则l[i],r[i]中就是与a[i]都不互素的左右两端的第一个数的位置.

接下来我们将维护一个A[i]树状数组,使得sum(R)-sum(L-1)就是我们所求的区间[L,R]的答案.

首先我们将所有区间查询读入,然后按L从小到大排序.

第一步:我们把所有l[i]<1(即这个a[i]左边全是和它互素的数)的i位置加1,然后在r[i](要求r[i]<=n)位置减1,即add(i,1),add(r[i],-1).注意这里在i位置上的值是a[i]是否独立的主导,而在r[i]上的值是a[i]是否独立的辅助,当a[r[i]]作为主导的时候,另外考虑即可.---查询结果前的预处理操作

做完这步,我们保证对所有区间[1,R](R<=n可以任意,但是区间起点必须是1)的查询结果就是:sum(R)-sum(0).(自己分l[i]<1和l[i]>=1两种情况验证一下,看看是不是这样)-----------------------查询结果

第二步:我们将对区间[2,R]求结果,因为之前已经求出了[1,R]的结果就是sum(R)-sum(0),所以我们现在求[2,R]区间的查询结果需要消除a[1]对区间[2,R]的影响.因为a[1]的r[1]肯定在1后面,之前第一步我们执行了add(i,1),add(r[i],-1)操作,所以现在我们需要还原,所以执行:

add(i,-1)和add(r[i],1).--------------------------------查询结果后的清除操作

执行完这两个操作就等于是a[1]永久的从a[n]数组内消失了,我们现在是计算[2,R]区间的,我们也可以认为2就是a[n]数组的起点,a[1]从来没有存在过.所以以a[2]作为数组新的起点的话,现在又有两类a[i]需要处理,第一类是l[i]==1的,另一个类是l[i]>1的.

l[i]>1的不用管,因为我们现在求的是区间[2,R](其实这类似于我们第一步处理区间[1,R]的时候对于l[i]>=1的a[i]不处理一样,自己思考一下是不是?)

对于l[i]==1的a[i],那么就需要执行操作add(i,1)和add(r[i],-1)(要求r[i]<=n),执行完了这一步我们保证对于区间[2,R]的任意查询结果就是:

sum(R)-sum(1)

需要用一个vector来保存vec[ l[i] ]=I,来执行查询结果前的预处理操作.

接下来以此类推即可.

然后来说说我们如何求出所有a[i]的l[i]和r[i]呢?下面只说r[i]的求法,l[i]的求法类似.:

首先我们定义一个数组d[MAXN]=n+1,其中d[x]=n+1表示含有因子x的第一个a[i]在n+1位置.然后我们从第n个数开始扫描,在扫描a[n]之前我们还没有得到任何一个因子,所以所有的d[x]都是=n+1.然后我们对a[n]质因数分解,得到了它所有的素因子y,此时我们就可以令d[y]=n了.然后我们又用d[y]的值去更新n-1的r[n-1]值.以此类推.

AC代码:1890ms

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=200000+100;
int prime[MAXN+1];
int getPrime()//得到小于等于MAXN的所有素数,prime[0]存放的是个数
{
    memset(prime,0,sizeof(prime));//prime[0]计数,prime[i]既用来表示第i个素数,又用来表示是否被访问
    for(int i=2; i<=MAXN; i++)
    {
        if(!prime[i]) prime[++prime[0]]=i;
        for(int j=1; j<=prime[0]&&prime[j]<=MAXN/i; j++)
        {
            prime[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
    return prime[0];
}
long long factor[100][2];
int facCnt;
int getFactors(long long x)
{
    //把x进行素数分解,分解成360=2^3*3^2*5^1。
    //factor[i][0]为第i+1个素因子的值,factor[i][1]为第i+1个素因子的幂
    facCnt=0;
    long long tmp=x;
    for(int i=1; prime[i]<=tmp/prime[i]; i++)//tmp至少为当前素数的平方才继续
    {
        factor[facCnt][1]=0;
        if(tmp%prime[i]==0)
        {
            factor[facCnt][0]=prime[i];
            while(tmp%prime[i]==0)
            {
                factor[facCnt][1]++;
                tmp/=prime[i];
            }
            facCnt++;
        }
    }
    if(tmp!=1)
    {
        factor[facCnt][0]=tmp;
        factor[facCnt++][1]=1;
    }
    return facCnt;
}
int c[MAXN+1];
int lowbit(int x){return x&(-x);}
int sum(int x)
{
    int res=0;
    while(x)
    {
        res += c[x];
        x-=lowbit(x);
    }
    return res;
}
void add(int x,int v)
{
    while(x<=MAXN)
    {
        c[x] +=v;
        x +=lowbit(x);
    }
}
struct node
{
    int l,r;
    int index;
    bool operator <(const node &b)const
    {
        return l<b.l;
    }
}nodes[MAXN];//存查询命令
int ans[MAXN];//存查询结果
int a[MAXN];//存初始n个数据
int b[MAXN];//b[i]表示含有因子i的第一个数的位置
int l[MAXN],r[MAXN];//与a[i]的值不互素的左边第一个数是a[l[i]],右边第一个数是a[r[i]]
vector<int> vec[MAXN];
int pp[MAXN][20];//pp[i][0]=x表示a[i]有x个素因子,pp[i][j]表示a[i]的第j个素因子
int main()
{
    getPrime();//记得要先算素数,才可以支持后面的素因子分解
    int n,m;
    while(scanf("%d%d",&n,&m)==2&&n&&m)
    {
        for(int i=0;i<=n+1;i++)vec[i].clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }

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

        for(int i=2;i<MAXN;i++)b[i]=n+1;//含有因子i的数 最早出现的位置
        for(int i=n;i>=1;i--)
        {
            pp[i][0]=getFactors(a[i]);//pp用来加速后面对l[i]的处理
            int pos=n+1;
            for(int j=0;j<facCnt;j++)
            {
                pos = min(pos,b[factor[j][0]]);
                b[factor[j][0]]=i;
                pp[i][j+1]=  factor[j][0];
            }
            r[i]=pos;
        }

        for(int i=2;i<MAXN;i++)b[i]=0;//含有因子i的数 最早出现的位置
        for(int i=1;i<=n;i++)
        {
            //getFactors(a[i]);
            int pos=0;
            facCnt= pp[i][0];
            for(int j=0;j<facCnt;j++)
            {
                factor[j][0] = pp[i][j+1];
                pos = max(pos,b[factor[j][0]]);
                b[factor[j][0]]=i;
            }
            l[i]=pos;
            vec[l[i]].push_back(i);
        }

        memset(ans,0,sizeof(ans));
        memset(c,0,sizeof(c));
        int p=1;//指针,指向当前处理到第几个查询
        for(int i=1;i<=n;i++)//当前处理第i个元素
        {
            if(p>m)
                break;
            for(unsigned int k=0;k<vec[i-1].size();k++)
            {
                int x=vec[i-1][k];//l[x]=i-1,所以需要处理
                add(x,1);
                add(r[x],-1);
            }//这步处理完,我们保证[i,R]区间的答案是sum(R)-sum(i-1)

            while(p<=m&&nodes[p].l==i)
            {
                ans[nodes[p].index]= sum(nodes[p].r)-sum(nodes[p].l-1);
                p++;
            }//这步是完成[i,R]的所有查询

            add(i,-1);//永久清除a[i]及其带来的影响,从i+1开始(从新开始),等于序列中从来没有过a[i]
            add(r[i],1);

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值