Yaroslav and Divisors

Codeforces Round #182 (Div. 1) D:http://codeforces.com/contest/301/problem/D

题意:给一个1-n,n个数的序列,然后查询一个区间[l,r],问这个区间内有多少对:一个数是另外一个数的约数。

题解:这样的题目做的太少,自己也知道要用离线的数据结构,但是始终想不来,看了别人的代码也是半天没有看懂,最后还是请教了别人,才稍微明白一点。首先,对于[l,r]之间的数对来说,可以把1--r的数对减去1--l-1的数对,这是肯定的,但是这样还是多算了一部分,因为1--l-1之间的数和l--r之间的数也可以构成数对,所以必须把这部分减去。怎么减去呢,这里才是关键。首先,对于一次查询,把l和r分开来操作。我们可以枚举i,i从1到n,当枚举到i的时候,i之前的所有的数的倍数更新了,也就是说i之前的数对l--r之间的数已经形成,并且此时对l--r之间影响只有1--l-1之间的数,如果,我们把这部分数对减去的话,那么之前说的关键问题就解决了。减去之后就可以更新第i个数的倍数了,然后当i是查询的右端点的时候,这时候普就可以直接查询右端点,然后查询左端点然后做差累加上去,就可以得到最终的答案了。统计和更新直接用树状数组来维护就可以了。这道题,自己不会打,看了别人的代码,然后自己敲了一遍,确实是一道好题,值得好好品味。自己这方面的思维还没有培养起来呢,以后要多练练。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<vector>
 6 using namespace std;
 7 const int N=2e5+10;
 8 int n,m;
 9 int a[N],ct[N];
10 int pos[N],l[N],r[N],ans[N];
11 vector<int>ll[N],rr[N];
12 void init(){
13    memset(a,0,sizeof(a));
14    memset(ct,0,sizeof(ct));
15    memset(pos,0,sizeof(pos));
16 }
17 int lowbit(int x){
18   return x&(-x);
19 }
20 void add(int x){
21   while(x<=n){
22     ct[x]++;
23     x+=lowbit(x);
24   }
25 }
26 int getsum(int x){
27     int ans=0;
28     while(x>0){
29         ans+=ct[x];
30         x-=lowbit(x);
31     }
32    return ans;
33 }
34 int main(){
35    while(~scanf("%d%d",&n,&m)){
36       init();
37     for(int i=1;i<=n;i++){
38        scanf("%d",&a[i]);
39        pos[a[i]]=i;
40     }
41     for(int i=1;i<=m;i++){
42         scanf("%d%d",&l[i],&r[i]);
43         ll[l[i]].push_back(i);
44         rr[r[i]].push_back(i);
45     }
46     for(int i=1;i<=n;i++){
47     for(int j=0;j<ll[i].size();j++){
48         ans[ll[i][j]]-=getsum(r[ll[i][j]])-getsum(i-1);
49     }
50     for(int j=1;j*a[i]<=n;j++){
51         add(pos[j*a[i]]);
52     }
53      for(int j=0;j<rr[i].size();j++){
54         ans[rr[i][j]]+=getsum(i)-getsum(l[rr[i][j]]-1);
55      }
56     }
57    for(int i=1;i<=m;i++){
58     printf("%d\n",ans[i]);
59    }
60  }
61 }
View Code

 

转载于:https://www.cnblogs.com/chujian123/p/3886640.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值