[CF]301D Yaroslav and Divisors

CF 301D Yaroslav and Divisors 

题意:给出n的一个排列,每次询问一个区间内有多少对数满足一个数是另一个数的因数。(n<=2e5)

将询问离线排序后从左往右扫,每次对于当前位置的数找它所有倍数出现的位置p,如果p<= i,则在i位置上加一,否则在p位置上记录i,当再次扫到p时在i位置上加一。每次扫描到区间右端点时用树状数组统计答案。

#include<bits/stdc++.h>
#define N 200020
 
using namespace std;
 
int a[N], b[N], c[N], r[N], l[N], ans[N], n, m;
vector<int> ask[N];
vector<int> d[N];
 
void add(int x)
{
    while(x<= n)
    {
        c[x]++;
        x+= x&-x;
    }
}
 
int sum(int x)
{
    int s= 0;
    while(x)
    {
        s+= c[x];
        x-= x&-x;
    }
    return s;
}
 
int main()
{
    while(scanf("%d%d", &n, &m)== 2)
    {
        for(int i= 1; i<= n; i++)
        {
            scanf("%d", &a[i]);
            b[a[i]]= i;
        }
        for(int i= 1; i<= m; i++)
        {
            scanf("%d%d", &l[i], &r[i]);
            ask[r[i]].push_back(i);
        }
        for(int i= 1; i<= n; i++)
        {
            for(int j= 0; j< (int)d[i].size(); j++) add(d[i][j]);
            for(int j= 1; j* a[i]<= n; j++)
            {
                int p= b[j* a[i]];
                if(p<= i) add(p);
                else d[p].push_back(i);
            }
            for(int j= 0; j< (int)ask[i].size(); j++)
            {
                int p= ask[i][j];
                ans[p]= sum(r[p])- sum(l[p]- 1);
            }
        }
        for(int i= 1; i<= m; i++) printf("%d\n", ans[i]);
    }
    return 0;
}

 

posted on 2019-03-28 18:19  枫棠  阅读(202)  评论(0编辑  收藏  举报

导航