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

题意:给出n个数,有m个查询,每次查询询问区间[L,R]中最多有多少个数与区间中其他数都不互质。

思路:首先预处理一下,把每个数的因子求出来(不包含1,因为1和任何数都互质),这个比较简单……然后再预处理一下第i个数向左最小的和它不互质的数的位置和第i个数向右和它不互质的数的位置(用lf[i]和rn[i]表示)。接下来的操作比较烦,不太好理解。先说下大体思路吧,把所有操作存起来,然后从左到右依次向树状数组里插入,我开始想的是在树状数组每个位置有一个数,一个区间的不互质的和就是区间的和,这样就要让区间的某些位置加一些数,另一些位置减一些数,这个是肯定能构造出来的。那么如何去做呢。

        先看一下这个数据吧:                        3   6   1   2   5   3

        就第4个数来说,构造出来的数就是:0  -1   1    1

         这样对于区间为i的查询就可以求出答案了。

        构造这个序列的方法如下:

        ①在第i位+1,在lf[i]-1处-1(这样区间左边在i和lf[i]之间就会加上1)

        ②从优先队列中取数,如果它的位置等于当前位置,那么就把队列中那个数的位置j处-1,在lf[j]-1处+1。

            解释一下这个操作,还以上面那组数据为例,如果只是第一个操作的话,执行完以后的数列是这样的:

           3   6   1   2   5   3

           0   0   1    1

           这样的话,当查询[2,4]时答案就不对了,这是由于这种操作只能保证之前的是正确的,这样就可能要把之前的操作“取消”,答案才会是正确的,因此,对于一个数,当操作到rn[i]+1的位置,就要把这个数之前的操作“取消”,也就是和①进行相反的操作。我用的优先队列存的右边要操作的位置,其实怎么搞都行,不一定要用优先队列,可以用vector存的,当时顺手写了个优先队列,所以……

       ③看懂第②歩,第三步就和明显了,把rn[i]+1加入优先队列。

 

 

      之前用线段树写的,后来数据加强线段树就T了,然后想用vector代替优先队列,这样可以把优先队列那儿O(nlog(n))的复杂度变成O(n),结果MLE了……Orz,没办法,写了发树状数组1900ms+过掉了……果然树状数组比线段树快很多,也可能是我线段树写渣了。。。

 

 

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=200000+10;
int pre[maxn],num[maxn],ans[maxn];
int lf[maxn],rn[maxn];
int sum[maxn];
int n,m;
struct Node
{
    int l,pos;
    bool operator <(const Node &a) const
    {
        return l>a.l;
    }
};
vector<int>vt[maxn];
vector<Node>querys[maxn];
int lowbit(int x)
{
    return x&-x;
}
int Query(int x)
{
    int ret=0;
    while(x>0)
    {
        ret+=sum[x];
        x-=lowbit(x);
    }
    return ret;
}
void add(int x,int v)
{
    while(x<=n)
    {
        sum[x]+=v;
        x+=lowbit(x);
    }
}
void solve()
{
    memset(pre,0xff,sizeof(pre));
    memset(sum,0,sizeof(sum));
    int sz,ntmp,pp;
    for(int i=1;i<=n;++i)
    {
        sz=vt[num[i]].size();
        pp=1;
        for(int j=0;j<sz;++j)
        {
            ntmp=vt[num[i]][j];
            if(pre[ntmp]!=-1) pp=max(pp,pre[ntmp]+1);
            pre[ntmp]=i;
        }
        lf[i]=pp;
    }
    memset(pre,0xff,sizeof(pre));
    for(int i=n;i>=1;--i)
    {
        sz=vt[num[i]].size();
        pp=n;
        for(int j=0;j<sz;++j)
        {
            ntmp=vt[num[i]][j];
            if(pre[ntmp]!=-1) pp=min(pp,pre[ntmp]-1);
            pre[ntmp]=i;
        }
        rn[i]=pp;
    }
    Node node,tmp;
    int x,y;
    priority_queue<Node>q;
    for(int i=1;i<=n;++i)
    {
        add(i,1);
        x=lf[i]-1;
        if(x) add(x,-1);
        while(!q.empty())
        {
            tmp=q.top();
            if(tmp.l==i)
            {
                q.pop();
                add(tmp.pos,-1);
                x=lf[tmp.pos]-1;
                if(x) add(x,1);
            }
            else break;
        }
        y=rn[i]+1;
        if(y<=n)
        {
            tmp.l=y;tmp.pos=i;
            q.push(tmp);
        }
        sz=querys[i].size();
        for(int j=0;j<sz;++j)
        {
            node=querys[i][j];
            ans[node.pos]=Query(i)-Query(node.l-1);
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    for(int i=2;i<=200000;++i)
      for(int j=i;j<=200000;j+=i)
        vt[j].push_back(i);
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        for(int i=1;i<=n;++i)
          scanf("%d",&num[i]);
        for(int i=0;i<=n;++i) querys[i].clear();
        int l,r;
        Node tmp;
        for(int i=0;i<m;++i)
        {
            scanf("%d%d",&l,&r);
            tmp.l=l;tmp.pos=i;
            querys[r].push_back(tmp);
        }
        solve();
        for(int i=0;i<m;++i)
          printf("%d\n",ans[i]);
    }
    return 0;
}


 


 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值