zoukankan      html  css  js  c++  java
  • 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
    ",ans[i]);
    59    }
    60  }
    61 }
    View Code
  • 相关阅读:
    【转】由浅入深表达式树(一)创建表达式
    【转】背后的故事之
    【转】背后的故事之
    【转】C#集合类型大盘点
    【转】最近用Timer踩了一个坑,分享一下避免别人继续踩
    【转】《深入理解C# 3.x的新特性》博文系列汇总
    【转】文件各种上传,离不开的表单
    【转】文件下载之断点续传(客户端与服务端的实现)
    【转】权限管理学习 一、ASP.NET Forms身份认证
    【转】C#单元测试,带你快速入门
  • 原文地址:https://www.cnblogs.com/chujian123/p/3886640.html
Copyright © 2011-2022 走看看