4792. 【NOIP2016提高A组模拟9.21】整除

29 篇文章 13 订阅
4 篇文章 0 订阅

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值