https://nanti.jisuanke.com/t/41391
2018那个1e5的题我莫队加树状数组随便过
今天莫队加lower_bound常数优化全加上了都卡不过去,思维限制死在莫队里了,没出来。
我们知道对于每个数,他只在他的倍数配在一起,它才贡献1,那么他的倍数可能在他的右边也有可能在左边
偶像 教我把询问拆成2部分,分开统计每个区间中的每个数与它左边的成对贡献情况和右边的成对贡献情况
先从1-n,枚举每个a[i]的倍数,如果倍数的位置在i的左边,那么在哪个位置上+1。
如果当前i这个位置有个右端点在这的询问(l,r),即 i==r,那么这个区间(l,r)的左半部分答案就是当前数组中[l,r]的和,因为当前[l,r]中每个位置i的值都是a[i]是在[i+1,r]的某个a[j]的倍数,才会在i这个地方加1。
从1到n在询问的右区端点统计答案,反过来的时候就在左端点统计答案。
#include<bits/stdc++.h>
#define maxl 100010
using namespace std;
int n,m;
int a[maxl],b[maxl],dy[maxl];
struct node
{
int l,r,id;
};
int ans[maxl];
vector <node> lq[maxl],rq[maxl];
inline void prework()
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dy[a[i]]=i;
lq[i].clear();rq[i].clear();
}
int l,r;
for(int i=1;i<=m;i++)
{
ans[i]=0;
scanf("%d%d",&l,&r);
lq[l].push_back(node{l,r,i});
rq[r].push_back(node{l,r,i});
}
}
inline void add(int i)
{
while(i<=n)
{
b[i]++;
i+=i&-i;
}
}
inline int sum(int i)
{
int ret=0;
while(i)
{
ret+=b[i];
i-=i&-i;
}
return ret;
}
inline void mainwork()
{
for(int i=1;i<=n;i++)
b[i]=0;
for(int i=1;i<=n;i++)
{
for(int j=a[i];j<=n;j+=a[i])
if(dy[j]<i)
add(dy[j]);
int l=rq[i].size();
for(int j=0;j<l;j++)
ans[rq[i][j].id]+=sum(i)-sum(rq[i][j].l-1);
}
for(int i=1;i<=n;i++)
b[i]=0;
for(int i=n;i>=1;i--)
{
for(int j=a[i];j<=n;j+=a[i])
if(dy[j]>i)
add(dy[j]);
int l=lq[i].size();
for(int j=0;j<l;j++)
ans[lq[i][j].id]+=sum(lq[i][j].r)-sum(i-1);
}
}
inline void print()
{
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
prework();
mainwork();
print();
}
return 0;
}