【Codeforces 301D】Yaroslav and Divisors | 树状数组、顺序统计

题目链接:https://codeforces.com/contest/301/problem/D

题目大意:

给出一个n的全排列,m次询问,每次询问区间[x,y]内,有多少对(a,b)满足a%b == 0

题目思路:

首先可以确定的是,a%b == 0满足因子关系

因为n为全排列,所以可以通过筛法把 因子关系都确定

之后假设 x在i位置,x的倍数在k位置

可以把[i,k]看为一个合法区间

所以题目变成了 询问在一个区间内,完全包含多少个合法区间

就有了顺序统计的思想

枚举右端点,利用记录左端点的贡献

因为从小到大枚举的右端点,所以左端点的贡献是合法的

询问时 输出GetSum(y)-GetSum(x-1),这样如果有区间部分在[x,y]部分不在[x,y],这样就会被同时减去

所以成立!

Code:

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(2)
#define debug(x) cout<<#x<<":"<'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll st[maxn];
int pos[maxn];
ll sum[maxn];
vector>q[maxn];
vectorv[maxn];
ll ans[maxn];
void update(int pos){
    while(pos<=n){
        sum[pos]++;
        pos += pos&-pos;
    }
}
ll GetSum(int pos){
    ll ans = 0;
    while(pos>0){
        ans += sum[pos];
        pos -= pos&-pos;
    }return ans;
}
int main(){
    read(n);    read(m);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        pos[x] = i;
    }
    for(int i=1;i<=n;i++){
        for(int k=i;k<=n;k+=i){
            int x = pos[i],y = pos[k];
            if(x>y) swap(x,y);
            v[y].push_back(x);
        }
    }

    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        q[y].push_back({x,i});
    }
    for(int i=1;i<=n;i++){
        for(int x:v[i]) update(x);
        for(auto x:q[i])
            ans[x.second] = GetSum(i) - GetSum(x.first-1);
    }
    for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    printf("\n");
    return 0;
}
/**
5 3
1 3 5 6 8
**/

 

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