2200专项:301D - Yaroslav and Divisors(树状数组 区间整除对数)

原题: http://codeforces.com/problemset/problem/301/D

题意: 有一个1-n的全排列,m个询问,某个区间的整除对对数(1-1,2-4,1-2)

解析:

首先可以简单的得出下标1~i内的对数:枚举每个数的倍数,标记位置较大的那个,再做前缀和。

区间 [ L , R ] [L,R] 的对数= [ 1 , R ] [ 1 , L 1 ] ( [ 1 , L 1 ] [ L , R ] ) [1,R]-[1,L-1]-([1,L-1]与[L,R]之间) 。现在就是处理这个之间的影响。

从小到大for,有一对 [ i , r ] [i,r] ,则树状数组 r + + r++ ,这个就是 [ 1 , L 1 ] [1,L-1] 的影响。那么 [ 1 , L 1 ] [1,L-1] [ L , R ] [L,R] 的影响就是 Q u e r y ( R ) Q u e r y ( L 1 ) Query(R)-Query(L-1)

#include<bits/stdc++.h>
using namespace std;

const int maxn=2e5+5;
int sum[maxn];
int pos[maxn];
vector<int>R[maxn];
int tr[maxn];
void add(int p,int v,int n){
    while(p<=n){
        tr[p]+=v;
        p+=p&-p;
    }
}
int query(int p){
    int res=0;
    while(p){
        res+=tr[p];
        p-=p&-p;
    }
    return res;
}

struct node{
    int l,r,id;
    bool operator<(const node &R)const{
        return l<R.l;
    }
}e[maxn];
int sub[maxn];

int main(){
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1,tmp;i<=n;i++)scanf("%d",&tmp),pos[tmp]=i;
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j+=i){
            if(pos[j]){
                int l=min(pos[i],pos[j]),r=max(pos[i],pos[j]);
                R[l].push_back(r);
                sum[r]++;
            }
        }
    }
    for(int i=1;i<=n;i++)sum[i]+=sum[i-1];

    for(int i=1;i<=m;i++)scanf("%d%d",&e[i].l,&e[i].r),e[i].id=i;
    sort(e+1,e+1+m);
    int ar=1;
    for(int i=1;i<=n;i++){
        while(ar<=m&&e[ar].l==i)
            sub[e[ar].id]=query(e[ar].r)-query(e[ar].l-1),++ar;
        for(int j=0;j<R[i].size();j++)
            add(R[i][j],1,n);
    }
    for(int i=1,l=e[1].l,r=e[1].r; i<=m; ++i,l=e[i].l,r=e[i].r)
        sub[e[i].id]=sum[r]-sum[l-1]-sub[e[i].id];
    for(int i=1;i<=m;i++)
        printf("%d\n",sub[i]);
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/90031561
今日推荐