【NOIP模拟】整除

题面

麦克雷有一个 1~n 的排列,他想知道对于一些区间,有多少对区间内的数(x,y),满足 x 能被 y 整除。
第一行包含 2 个正整数 n,m。表示有 n 个数,m 个询问。
30%:1<=n,m<=100 100%:1<=n,m<=2*1e5 ,1<=pi<=n。

分析

一看就是线段树or树状数组题,和以前那个mushroom的区间画风真的像
1到n的排列也是个很特殊的信息。其实很容易想到把每个数的倍数预处理出来,再通过区间相减求答案。
然而,这种做法忽略了,你要查询的区间中的一个数的因数或倍数在这个区间之外。所以,为了防止这种情况,我们需要固定右端点。
右端点升序排列,处理出所有在右端点的数范围内的倍数和因数,在线查询,在线更新即可。
先开始想的枚举每个数的因数是√n * n 的时间复杂度,然而n/1+n/2+n/3+n/4+……+n/n = nlogn,所以总复杂度O((nlogn+m)log n)

代码

#include<bits/stdc++.h>
using namespace std;
#define N 200020
#define ll long long
ll n,m,cnt=1;
ll a[N],t[N],pos[N];
struct email
{
    ll l,r,id;
    ll ans;
}q[N];
vector<ll>v[N];
bool cmp1(email x,email y){return x.r<y.r;}
bool cmp2(email x,email y){return x.id<y.id;}
inline ll lowbit(ll x){return x&(-x);}
inline void add(ll x)
{
    for(;x<=n;x+=lowbit(x))
        t[x]++;
}
inline ll query(ll x)
{
    ll ret=0;
    for(;x;x-=lowbit(x))
        ret+=t[x];
    return ret;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)scanf("%lld",&a[i]),pos[a[i]]=i;
    for(ll i=1;i<=m;i++)scanf("%lld%lld",&q[i].l,&q[i].r),q[i].id=i;
    sort(q+1,q+1+m,cmp1);
    for(ll i=1;i<=n;i++)
        for(ll j=i;j<=n;j+=i)
        {
            if(i!=j)v[pos[j]].push_back(i);
            v[pos[i]].push_back(j);
        }    
    for(ll i=1;i<=n;i++)
    {
        ll siz=v[i].size();
        for(ll j=0;j<siz;j++)
            if(pos[v[i][j]]<=i)
                add(pos[v[i][j]]);
        while(q[cnt].r==i)q[cnt].ans=query(q[cnt].r)-query(q[cnt].l-1),++cnt;
    }
    sort(q+1,q+1+m,cmp2);
    for(ll i=1;i<=n;i++)printf("%lld
",q[i].ans);
    return 0;
}
“Make my parents proud,and impress the girl I like.”
原文地址:https://www.cnblogs.com/NSD-email0820/p/9789074.html