【NOIP模拟】整除

Description

麦克雷有一个1~n的排列,他想知道对于一些区间,有多少对区间内的数(x,y),满足x能被y整除。

Solution

这道题我到这枚想出来…..打了个莫队又打错了TAT
我们先来分析一下题目。
可以理解为,x能做出的贡献就是他的倍数在区间中的存在个数。(因为找倍数是n log n的,但是找因数是n√n的)
那么我们可以拆成两部分,一个数x的倍数在前面的情况,另一个是x的倍数在后面的情况。
首先我们肯定要限制右端点,那么按右端点为第一关键字排一次序。
然后一个个点的加进来,如果现在加进来了一个a[r],那么找出所有的a[r]*k,如果a[r]*k的位置在r的左边(因为要求倍数在前面),那么树状数组就在a[r]*k的位置加一,表示a[r]*k这个点到r的这个范围内有一个数与他之间又贡献。那么答案很显然在树状数组中找[l,r]之间的和。
然后倍数在前面的处理完了,就处理在后面的情况,倒着做一遍(按左端点排一次序就好了)。
最后倒着做的时候把枚举k的范围从1开始枚举就好了,因为自己和自己也算一个点对。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=200007;
int i,j,k,n,m,ans;
int a[maxn],kuai[maxn],da,l,r,ans1[maxn],c[maxn];
bool bz[maxn];
int t[maxn];
struct node{
    int l,r,c;
}b[maxn];
bool cmp(node x,node y){
    return x.rbool cmp1(node x,node y){
    return x.lint lowbit(int x){return x&(-x);}
void add(int x,int y){for(;x<=n;x+=lowbit(x))t[x]+=y;}
int find(int x){
    int y=0;
    for(;x;x-=lowbit(x))y+=t[x];
    return y;
}
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n)scanf("%d",&a[i]),c[a[i]]=i;
    fo(i,1,m){
        scanf("%d%d",&b[i].l,&b[i].r);
        b[i].c=i;
    }
    sort(b+1,b+1+m,cmp);
    r=1;
    fo(i,1,m){
        fo(j,b[i-1].r+1,b[i].r){
            fo(k,2,n/a[j]){
                if(c[a[j]*k]<=j)add(c[a[j]*k],1);
            }
        }    
        ans1[b[i].c]+=find(b[i].r)-find(b[i].l-1);
    }
    memset(t,0,sizeof(t));
    sort(b+1,b+1+m,cmp1);
    b[m+1].l=n+1;
    fod(i,m,1){
        fod(j,b[i+1].l-1,b[i].l){
            fo(k,1,n/a[j]){
                if(c[a[j]*k]>=j)add(c[a[j]*k],1);
            }
        }
        ans1[b[i].c]+=find(b[i].r)-find(b[i].l-1);
    }
    fo(i,1,m)printf("%d\n",ans1[i]);
}

你可能感兴趣的:(noip,暴搜,树状数组)