The Preliminary Contest for ICPC Asia Xuzhou 2019 I query(离线,树状数组)

题目链接:query
题意:给出一个长度为n的全排列,q次询问,每次询问区间 [ l , r ] [l,r] [l,r]内有多少对数字 ( p , q ) (p,q) (p,q),满足p%q==0。
数据范围 n , q 同 阶 1 0 5 n,q同阶10^5 n,q105
数据范围保证了序列是一个全排列,那么可以预处理出所有的满足条件的数字对,这样的数字对数不会超过 n ∗ l o g 2 n n*log_2n nlog2n
枚举位置i,假设所有的数字对出现的位置 ( p , q ) , p < q (p,q),p<q (p,q),p<q,将所有的满足 q = = i q==i q==i的数字对的p位置处加1,对于右区间是i的询问,计算他的答案为ask( r )-ask( l-1 )。
参考自:https://blog.csdn.net/u014258433/article/details/75675233

#include
using namespace std;

const int maxn=1e5+7;

typedef long long ll;

int sum[maxn];
int n;
void add(int x,int y){
    for(;x<=n;x+=x&-x) sum[x]+=y;
}

int ask(int x){
    int res=0;
    for(;x;x-=x&-x) res+=sum[x];
    return res;
}

vector<pair<int,int> >query[maxn];

vector<int> p[maxn];

int pos[maxn];
int ans[maxn];
int main(){
    int q,x,y,l,r;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i){
        scanf("%d",&x);
        pos[x]=i;
    }
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;j+=i){
            x=pos[i],y=pos[j];
            if(x<y) swap(x,y);
            p[x].push_back(y);
        }
    for(int i=1;i<=q;++i){
        scanf("%d%d",&l,&r);
        query[r].push_back(make_pair(l,i));
        ans[i]-=r-l+1;
    }
    for(int i=1;i<=n;++i){
        for(int j=0;j<p[i].size();++j) add(p[i][j],1);
        for(int j=0;j<query[i].size();++j) ans[query[i][j].second]+=ask(i)-ask(query[i][j].first-1);
    }
    for(int i=1;i<=q;++i) printf("%d\n",ans[i]);

    return 0;
}

你可能感兴趣的:(树状数组)