Description
给定长度为 (n) 的数列,每个数都在 ([1,n]) 间,回答 (m) 个询问,每次给定一个区间 ([l,r]),问其中有多少对数间存在倍数关系。
Solution
考虑离线处理,将所有询问区间按右端点排序
用树状数组(单点修改,区间求和)动态维护每个位置的贡献,对于一对数,我们始终将贡献记录在位置靠前的那个数身上,记为 (f[i])
从左到右扫描整个序列,对于当前位置 (i) 上的数 (p_i),我们考虑它的所有倍数 (p_j)
若 (j<i),则 (f[ j ]+=1)
若 (j>i),则需要延迟处理,我们对每个位置维护一个无序集合(实现用 vector
),遇到这样的情况,就将 (i) 加入 (j) 的集合中,而每个点被扫描时,则会处理它集合中的所有 (k),将 (f[k]+=1)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 400005;
int n,m,l,r,p[N],pos[N],ans[N];
vector <int> v[N];
int ar[N]; // index: 1 ~ N
int lowbit(int t)
{
return t & (-t);
}
void add(int i, int v)
{
for (; i < N; ar[i] += v, i += lowbit(i));
}
int sum(int i)
{
int s = 0;
for (; i > 0; s += ar[i], i -= lowbit(i));
return s;
}
int query(int l,int r)
{
return sum(r)-sum(l-1);
}
struct request
{
int l,r,id;
bool operator < (const request& obj)
{
return r < obj.r;
}
} req[N];
signed main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>p[i], pos[p[i]]=i;
for(int i=1;i<=m;i++) cin>>req[i].l>>req[i].r, req[i].id=i;
sort(req+1,req+m+1);
int cur=1;
for(int _i=1;_i<=n;_i++)
{
int i=p[_i];
for(int k:v[_i]) add(k,1);
for(int j=i;j<=n;j+=i)
{
int ps=pos[j];
if(ps<=_i) add(ps,1);
else v[ps].push_back(_i);
}
while(cur<=m && req[cur].r==_i)
{
ans[req[cur].id]=query(req[cur].l,req[cur].r);
++cur;
}
}
for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
}