Description
麦克雷有一个1~n的排列,他想知道对于一些区间,有多少对区间内的数(x,y),满足x能被y整除。
Input
第一行包含2个正整数n,m。表示有n个数,m个询问。
接下来一行包含n个正整数,表示麦克雷有的数列。
接下来m行每行包含2个正整数l,r。表示询问区间[l,r]。
Output
共 m 行,每行一个整数,表示满足条件的对数。
Sample Input
10 9
1 2 3 4 5 6 7 8 9 10
1 10
2 9
3 8
4 7
5 6
2 2
9 10
5 10
4 10
Sample Output
27
14
8
4
2
1
2
7
9
Data Constraint
30%:1<=n,m<=100
100%:1<=n,m<=2*10^5,1<=pi<=n
Solution
答案=【1,R】的合法对数-【1,L-1】的合法对数-一个数属于 【1, L - 1】 另一个数属于 【L, R】 的合法对数。
换一种思路,对于一个区间【L,R】,我们将【1,L】所有对答案有贡献的点对(x,y)找出来,若其中x或y在L的左边则不合法,将答案减去。
所以,我们可以按照R排一遍序,并按原序列顺序从 1 往 n 做,每做到一个位置,就在它左边的数中与它有倍数关 系的数的位置加一。 并统计已经加的次数。 那么每当我们遇到一个右端点与当前做的位置相同时,就可以直接将当前总共加 的次数减去起点到左端点的区间的和就行了。
单点修改和区间求和可以用树状数组。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200010
#define ls x<<1
#define rs x<<1|1
using namespace std;
int n,m,ans[N],x,a[N],b[N],tot,t[N<<4],nx[N<<4],l[N],f[N];
struct node{int l,r,s;}q[N];
int cmp(node x,node y){return x.r<y.r;}
int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
void add(int x,int y){t[++t[0]]=y;nx[t[0]]=l[x];l[x]=t[0];}
void add(int x){
for(;x<=n;x+=x&-x) f[x]++;
}
int find(int x){
int sum=0;
for(;x;x-=x&-x) sum+=f[x];
return sum;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++){a[i]=read();b[a[i]]=i;}
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j+=i){
if(b[i]<b[j]) add(b[j],b[i]);
else add(b[i],b[j]);
}
}
for(int i=1;i<=m;i++) q[i]=(node){read(),read(),i};
sort(q+1,q+1+m,cmp);x=1;
for(int i=1;i<=n;i++){
for(int k=l[i];k;k=nx[k]){add(t[k]),tot++;}
while(q[x].r==i){
ans[q[x].s]=tot;
if (q[x].l!=1) ans[q[x].s]-=find(q[x].l-1);
x++;
}
if(x>m) break;
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/90943751