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的,所以结果就是
题意:有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
*/