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
数据范围保证了序列是一个全排列,那么可以预处理出所有的满足条件的数字对,这样的数字对数有 ∑ i = 1 n ⌊ x / i ⌋ \sum_{i=1}^n\lfloor x/i \rfloor i=1nx/i对, 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<bits/stdc++.h>
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;
}

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

const int maxn=1e5+7;

int a[maxn],pos[maxn];
vector<int> v[maxn];

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;
}

struct Node{
    int l,r,id;
    bool operator <(const Node& x)const{
        return r<x.r;
    }
}b[maxn];

int ans[maxn];
int main(){
    int m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        pos[a[i]]=i;
    }

    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;j+=i){
            int x=pos[i],y=pos[j];
            if(x<y) swap(x,y);
            v[x].push_back(y);
        }

    for(int i=1;i<=m;++i){
        scanf("%d%d",&b[i].l,&b[i].r);
        b[i].id=i;
        ans[i]-=b[i].r-b[i].l+1;
    }

    sort(b+1,b+1+m);

    for(int i=1,j=1;i<=m;++i){
        while(j<=b[i].r){
            for(int k=0;k<v[j].size();++k) add(v[j][k],1);
            ++j;
        }
        ans[b[i].id]+=ask(b[i].r)-ask(b[i].l-1);
    }
    for(int i=1;i<=m;++i) printf("%d\n",ans[i]);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值