题目链接:
hdu英文题面,牛客中文题面,一个意思。
题意:
有一个长度为 n 的序列,m 个询问。每个询问求区间内与所有数都互质的数的个数。
思路:
对于序列中每一个位置,用 l[i] 保存位置 i 左侧第一个与其不互质的数,用 r[i] 保存位置 i 右侧第一个与其不互质的数。
离线处理询问,把询问按右端点从小到大排序。使用树状数组维护不互质的数的个数,那么互质的数的个数等于区间长度减去不互质的数的个数。
树状数组的维护:遍历一遍序列,对于每个位置,用树状数组使其 l[i] 的位置上 +1 ,若这个点是位置 j 的右端点,那么使 j 的位置上 +1,在 l[j] 的位置上 -1 。若该位置为一个查询的右端点,计算答案。(树状数组更新的原理自己手算一遍能更好地理解,如算一下3 6 3 5 10 12这个样例)
对于每个查询用区间长度 - 树状数组求和即可得到答案。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 2e5+10;
const ll mod = 1000000007;
typedef struct{
int l,r,id;
}Point;
int n,m;
int a[MAX+10];
Point op[MAX+10];
int c[MAX+10];
int l[MAX+10],r[MAX+10];
int tmp[MAX+10];
bool vis[MAX+10];
int prime[MAX+10];
vector<int>mp[MAX+10];
vector<int>vec[MAX+10];
int ans[MAX+10];
void init()
{
for(int i=0;i<=n;i++){
l[i]=0;
r[i]=n+1;
vec[i].clear();
}
vec[n+1].clear();
memset(c,0,sizeof(c));
}
bool cmp(Point x,Point y)
{
return x.r<y.r;
}
void getprime()
{
memset(vis,0,sizeof(vis));
int num=0;
for(int i=2;i<=MAX;i++){
if(!vis[i]){
prime[++num]=i;
}
for(int j=1;j<=num&&i*prime[j]<=MAX;j++){
vis[i*prime[j]]=true;
if(i%prime[j]==0) break;
}
}
for(int i=1;i<=num;i++){
for(int j=1;j*prime[i]<=MAX;j++){
mp[j*prime[i]].push_back(prime[i]);
}
}
}
int lowbit(int x)
{
return x&(-x);
}
void add(int k, int x)
{
if(k==0){
return;
}
while (k <= n)
{
c[k] += x;
k += lowbit(k);
}
}
int sum(int x)
{
int val = 0;
while (x)
{
val += c[x];
x -= lowbit(x);
}
return val;
}
int main()
{
getprime();
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0) break;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&op[i].l,&op[i].r);
op[i].id=i;
}
init();
//注意tmp[ ]数组置0的范围为 0 - MAX
//因为a[i]质因数的范围在 0 - MAX
for(int i=0;i<=MAX;i++) tmp[i]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<mp[a[i]].size();j++){
l[i]=max(l[i],tmp[mp[a[i]][j]]);
tmp[mp[a[i]][j]]=i;
}
}
for(int i=0;i<=MAX;i++) tmp[i]=n+1;
for(int i=n;i>=1;i--){
for(int j=0;j<mp[a[i]].size();j++){
r[i]=min(r[i],tmp[mp[a[i]][j]]);
tmp[mp[a[i]][j]]=i;
}
}
for(int i=1;i<=n;i++){
vec[r[i]].push_back(i);
}
sort(op+1,op+m+1,cmp);
int pos=1;
for(int i=1;i<=m;i++){
while(pos<=n&&pos<=op[i].r){
add(l[pos],1);
for(int j=0;j<vec[pos].size();j++){
add(vec[pos][j],1);
add(l[vec[pos][j]],-1);
}
pos++;
}
ans[op[i].id]=op[i].r-op[i].l+1-(sum(op[i].r)-sum(op[i].l-1));
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
}
return 0;
}