codeforces 301D Yaroslav and Divisors

题目大意:

     给你n个数,然后问你区间内有几对数a,b(b%a=0)。

解决方法:

    将所有的区间都进行排序(按照左端点的大小),建立一个pre【i】数组,它表示i上一次出现在某个位置,pre【i】初始值为0.

    从右向左处理每个数字,将每个数字的因子与倍数k都求出来,然后树状数组进行add(pre[k],1)的操作。某段区间的答案就是sum(右端点)。

    为什么呢?想想如果k是i的因子或者倍数,i在pos1处,k在pos2处,那么所有区间包含pos1与pos2的都要加上1,我们又是左端点排序,所有直接求sum(右端点)就行了。

我的代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define maxn 1000000
using namespace std;
int pre[maxn];
long long tree[maxn];
struct Point {
  int x,y,id;
  long long ans;
}po[maxn];
int num[maxn];
int n,m;
int maxx;
int add(int x,long long v){
  for (int i=x;i<=n;i+=i&(-i))
      tree[i]+=v;
  return 0;
}
long long sum(int x){
  long long ans=0;
  for (int i=x;i>0;i-=i&(-i))
     ans+=tree[i];
  return ans;
}
int cmp(Point a,Point b){
  return a.y<b.y||a.y==b.y&&a.x<b.x||a.y==b.y&&a.x==b.x&&a.id<b.id;
}
int cmp1(Point a,Point b){
  return a.id<b.id;
}
int change(int n){
  for (int i=num[n]*2;i<=maxx;i+=num[n]){
      if (pre[i]) add(pre[i],1);
  }
  int k=(int)sqrt(num[n]);
  //cout<<"num "<<num[n]<<endl;
  for (int i=1;i<=k;i++)
    if(num[n]%i==0){
      //cout<<"*****"<<endl;
      if (i==k){
         if (k*k==num[n]){
        //    cout<<" i "<<i<<endl;
            if (pre[i]) add(pre[i],1);
         }
         else {
        //   cout<<" i "<<i<<endl;
         //  cout<<" i "<<num[n]/i<<endl;
           if (pre[i]) add(pre[i],1);
           if (pre[num[n]/i]) add(pre[num[n]/i],1);
         }
      }
      else {
      //  cout<<" i "<<i<<endl;
      //  cout<<" i "<<num[n]/i<<endl;
        if (pre[i]) add(pre[i],1);
        if (pre[num[n]/i]) add(pre[num[n]/i],1);
      }
    }
  add(n,1);
  pre[num[n]]=n;
  return 0;
}
int main (){
  //freopen("test.in","r",stdin);
  while (~scanf("%d%d",&n,&m)){
    memset(tree,0,sizeof(tree));
    for (int i=1;i<=n;i++) scanf("%d",&num[i]),pre[i]=0;
    for (int i=1;i<=m;i++){
        scanf("%d%d",&po[i].x,&po[i].y);
        po[i].id=i;
    }
    sort(po+1,po+m+1,cmp);
    int pos=1;
    maxx=0;
    for (int i=1;i<=m;i++){
        while (pos<=po[i].y&&pos<=n){
            change(pos);
            maxx=max(maxx,num[pos]);
            pos++;
        }
        if (po[i].x>1)po[i].ans=sum(po[i].y)-sum(po[i].x-1);
        else po[i].ans=sum(po[i].y);
    }
    sort(po+1,po+m+1,cmp1);
    for (int i=1;i<=m;i++)
        printf("%I64d\n",po[i].ans);
  }
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值