hdu4777 求区间内与其它数均互质的数的个数(树状数组+离线处理)

hdu4777 求区间内与其它数均互质的数的个数
题意:有n(1<=n<=200000)个数字,m(1<=m<=200000)个查询,每次询问[l,r]区间这些数字中,和区间内其他数都互质的数有多少个。
解题思路:
离线处理+树状数组,首先预处理出[1,200000]所有数的质因子,放到have[]里面,然后根据输入的n个数w[],求出每个数的它的质因子
出现的最左位置以及最右位置。然后对m个查询离线,按右边由小到大排序。如第二个样例:
    3   6   1   2   5   3
l[] 0   1   0   2   0   2
r[] 2   4   7   7   7   7
V[] {}  {1} {}  {2}         {3,4,5,6}
明显,求[left,right]区间的结果是(right-left+1-notFit),其中notFit=i的数量(l[i]>=left)+i的数量(r[i]<=right)-i的数量(l[i]>=left && r[i]<=right) ;
可以从左往右扫描,对于第i个,先add(l[i],1),因为左边的notFit数目多了1(自己),然后对V[i]集合里的数x=V[i][j],先add(x,1);因为
x的右边已经超出范围了,所以变为notFit,另外要add(l[x],-1);意思是减去右边notFit的并且左边也notFit的,所以结果就是

sum( right-left+1-( sum(right)-sum(left-1) ) ) 

参考资料:http://blog.csdn.net/ok_again/article/details/15235883

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<deque>
#include<bitset>
#define N 201005
using namespace std;
int w[N],l[N],r[N];//l[i],r[i]保存第i个数的质因子出现的最左位置以及最右位置
vector<int>V[N];//V[i]存储的是j,其中r[j]=i
vector<int>have[N],VPrime;//have[i]存储的是数字i分解的质因子,VPrime存储的是[1,200000]的素数
int ans[N];//存储结果
int flag[N];
struct node
{
    int left,right,id;
}p[N];
int cmp(node aa,node bb)
{
    return aa.right<bb.right;
}
int ar[N];
int lowb(int t)
{
    return t&(-t);
}
void add(int i,int v)
{
    if(i==0) return;
    for(;i<N;ar[i]+=v,i+=lowb(i));
}
int sum(int i)
{
    int s=0;
    for(;i>0;s+=ar[i],i-=lowb(i));
    return s;
}
void getHave(int index,int v)
{
    int i=0;
    while(v>1&&i<VPrime.size())
    {
        if(VPrime[i]*VPrime[i]>v)
        {
            have[index].push_back(v);
            break;
        }
        if(i<VPrime.size()&& v%VPrime[i]==0)
        {
            have[index].push_back(VPrime[i]);
        }
        while(i<VPrime.size()&& v%VPrime[i]==0)
        {
            v/=VPrime[i];
        }
        i++;
    }
}
bool prime[N];
void init()
{
    int i,j;
    memset(prime,0,sizeof(prime));
    prime[1]=prime[0]=1;
    for(i=2;i<=N-2;i++)
    for(j=2;i*j<=N-2;j++)
    {
        prime[i*j]=1;
    }
    VPrime.clear();
    for(i=2;i<=N-2;i++)
    {
        if(prime[i]==0)
            VPrime.push_back(i);
    }
    for(i=0;i<N;i++)
    {
        have[i].clear();
    }
    for(i=2;i<N;i++)
    {
        getHave(i,i);
    }
}
void init2(int n)//计算出l数组,r数组以及V[]
{
    for(int i=0;i<=n;i++)
    {
        V[i].clear();
    }
    memset(l,0,sizeof(l));
    memset(r,0,sizeof(r));
    memset(flag,0,sizeof(flag));
    for(int i=1;i<=n;i++)
    {
        int left=0;
        for(int j=0;j<have[w[i]].size();j++)
        {
            left=max(left,flag[have[w[i]][j]]);
        }
        l[i]=left;
        for(int j=0;j<have[w[i]].size();j++)
        {
            flag[have[w[i]][j]]=i;
        }
    }
    for(int i=1;i<N;i++)//这里要初始化为n+1
    {flag[i]=n+1;}
    for(int i=n;i>=1;i--)
    {
        int right=n+1;
        for(int j=0;j<have[w[i]].size();j++)
        {
            right=min(right,flag[have[w[i]][j]]);
        }
        r[i]=right;
        for(int j=0;j<have[w[i]].size();j++)
        {
            flag[have[w[i]][j]]=i;
        }
    }
    for(int i=1;i<=n;i++)
    {
        V[r[i]].push_back(i);
    }
}
int main()
{
    int i,j,k;
    int n,m,t;
    init();
    while(scanf("%d%d",&n,&m)!=EOF&&!(n==0&&m==0))
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&w[i]);
        }
        init2(n);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&p[i].left,&p[i].right);
            p[i].id=i;
        }
        sort(p+1,p+1+m,cmp);
        memset(ar,0,sizeof(ar));
        i=1;
        for(j=1;j<=m;j++)
        {
            while(i<=p[j].right)
            {
                add(l[i],1);//将左边notFit的+1
                for(k=0;k<V[i].size();k++)
                {
                    add(l[V[i][k]],-1);//将左边跟右边同时notFit的-1,去掉重复
                    add(V[i][k],1);//将右边notFit的+1
                }
                i++;
            }
            int notFit=sum(p[j].right)-sum(p[j].left-1);
            ans[p[j].id]=p[j].right-p[j].left+1-notFit;
        }
        for(i=1;i<=m;i++)
        {
            printf("%d\n",ans[i]);
        }
    }
}
/*
input:
3 2
2 1 4
1 2
1 3
6 4
3 6 1 2 5 3
1 3
4 6
4 4
2 6
0 0
output:
2
1
1
3
1
2
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值