The Preliminary Contest for ICPC Asia Xuzhou 2019 I. query

166 篇文章 0 订阅

      给你一组n个数字,这些数字是1~n的一个排列。现在有很多个询问,询问给定一个区间,问区间内有多少个整除对。
        说实话,根本想不到是用树状数组的题目……求整除对,怎么都像是数论的题目,为什么和树状数组有关。后来才知道统计的力量!
        首先,我们设sum[i],表示区间[1,i]的整除对数量,自然而然的,我们就会认为对于区间[l,r],它的整除对数量可能是sum[r]-sum[l-1],我们不妨设这个数字为x。当然了,整除对数目肯定不是这么简单的,因为显然我们多算了一个数字在[1,l]区间,而另一个数字在[l,r]区间内的整除对。那么我们要做的就是要消除这个多余的,不妨设这个多余的部分为y,则最后结果就是x-y。
        对于一个数字i,他在1~n内的倍数有n/i个,意味着对应i的倍数都可以与他组成整除对。那么我们便可以利用这个性质计算之前所说的y。顺序枚举这个i,对于q.l=i的询问,sum[q.r]-sum[q,l-1]就是y,然后再把所有i的倍数的sum值加1。这样子说可能不太好理解,自己用一组数据尝试就会知道,其实y就是在q.l和q.r之间的数字的倍数没有加进sum之前的值,然后每次把位置i的数字的倍数加入sum中。至于x,则是类似,当q.r=i的时候,即区间右端点前所有数字的倍数已经加进sum中之后,x=sum[q.r]-sum[q,l-1],因为再往后的值对该区间已经不会再产生影响了。
        现在已经很明显了,需要以个支持单点修改,区间查询的数据结构,树状数组为上策。然后具体写的时候也有点讲究的,不然在枚举区间的时候容易把复杂度退化到O(N^2)具体代码如下:(本段话借鉴自:https://blog.csdn.net/u013534123/article/details/75208828

 

AC 代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int M=1e5+50;
int t[M],m,n,a[M],pos[M];
ll ans[M];
void add(int x,int v){ while(x<=n) t[x]+=v,x+=(x&-x);}
ll sum(int x){
    ll res=0;
    while(x)res+=t[x],x-=(x&-x);
    return res;
}
struct Node{int id,l,r;}q1[M],q2[M];
bool cmp(Node a,Node b){return a.l==b.l?a.r<b.r:a.l<b.l;}
bool cmp1(Node a,Node b){return a.r==b.r?a.l<b.l:a.r<b.r;}
int main(){
    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<=m;i++) scanf("%d%d",&q1[i].l,&q1[i].r),q1[i].id=i,q2[i]=q1[i];
    sort(q1+1,q1+m+1,cmp);
    sort(q2+1,q2+m+1,cmp1);
    for(int i=1,j=1,k=1;i<=n;i++){
        while(j<=m&&q1[j].l==i) ans[q1[j].id]-=sum(q1[j].r)-sum(q1[j].l-1),j++;
        for(int x=a[i]+a[i];x<=n;x+=a[i]) add(pos[x],1);
        while(k<=m&&q2[k].r==i) ans[q2[k].id]+=sum(q2[k].r)-sum(q2[k].l-1),k++;
    }
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值