『树状数组』整除

题目描述

在这里插入图片描述

题解

思维难度好大的题…

我们思考如何对于 a [ i ] a[i] a[i]来说,有多少对合法的 ( a [ i ] , a [ j ] ) ( j &lt; i ) (a[i],a[j])(j&lt;i) (a[i],a[j])(j<i).

我们可以预处理出所有a[i]的因数和倍数,并存储来一个数组内;当求解a[i]时,我们就把所有位置小于等于i(不计算大于是为了避免重复计算)的所有 a [ i ] a[i] a[i]的因数或倍数都存进树状数组里。如果要求解区间 [ L , R ] [L,R] [L,R],只要 a s k ( R ) − a s k ( L ) ask(R)-ask(L) ask(R)ask(L)即可。

同理,我们需要 [ L , R ] [L,R] [L,R]中所有的对数,只需要 a s k ( R ) − a s k ( L ) ask(R)-ask(L) ask(R)ask(L)即可;因为每一个数事实上是单独计算的,仅仅利用树状数组来维护一个整体累加。这样,我们就能够在枚举i的时候求解任意 q [ k ] = r q[k]=r q[k]=r的答案了。

最后,我们只要将每一个询问离线一下,按照右端点从左到右排序即可。

代码如下:

#include <bits/stdc++.h>

using namespace std;

int n,m;
int a[300000];
int pos[300000];
int ans[300000];
vector <int> big[300000];
vector <int> sma[300000];
struct node 
{
	int l, r, id;
	friend bool operator < (node p1, node p2) {
		return p1.r < p2.r;
	}
};
node q[300000];

struct Tree
{
	int S[1000000];
	#define lowbit(i) (i&-i)
	void add(int x,int v)
	{
		for (int i=x;i<=n;i+=lowbit(i))
		    S[i] += v;
		return;
	}
	int ask(int x)
	{
		int sum = 0;
		for (int i=x;i>=1;i-=lowbit(i))
		    sum += S[i];
		return sum;
	}
} tree;

inline int read(void)
{
	int s = 0, w = 1; char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
	return s * w;
}

void Get(int x)
{
	for (int i=x;i<=n;i+=x) 
	{
		if (i != x) big[x].push_back(i);
		sma[i].push_back(x);
	}
	return;
}

int main(void)
{
	freopen("divisor.in","r",stdin);
	freopen("divisor.out","w",stdout);
	n =read(), m = read();
	for (int i=1;i<=n;++i) a[i] = read(), pos[a[i]] = i;
	for (int i=1;i<=m;++i) q[i].l = read(), q[i].r = read(), q[i].id = i;
	sort(q+1, q+m+1);
	for (int i=1;i<=n;++i) Get(i);
	int now = 1;
	for (int i=1;i<=n;++i)
	{
		for (int j=0;j<big[a[i]].size();++j)
		{
			int num = big[a[i]][j];
			if (pos[num] <= i) tree.add(pos[num],1);
		}
		for (int j=0;j<sma[a[i]].size();++j)
		{
			int num = sma[a[i]][j];
			if (pos[num] <= i) tree.add(pos[num],1);
		}
		while (q[now].r == i && now <= m) 
			ans[q[now].id] = tree.ask(q[now].r)-tree.ask(q[now].l-1), now ++;
	}
	for (int i=1;i<=m;++i) printf("%d\n", ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值