hdu5288 区间个数

来源:互联网 发布:飞天网络 编辑:程序博客网 时间:2024/06/08 19:22

题意:给定一个n个数的序列,定义一个函数f(l, r),表示在[l, r]这个区间内,满足不是区间内任何其他数的倍数的数的个数。比如一个区间内的数为2,3,4,那么函数值为2。因为2和3都不是其他任何数的倍数,但4是2的倍数。现在要求所有的区间的函数值的和。

思路:相对于计算每个区间内有多少个这样的数,我们不如来看每个数被计算了多少次。每个数左面第一个不是他约数的数到右面第一个不是它约数的数,这样一个区间里的所有子区间 这个数都会被计算一次。设这个数的位置是pos,左右的那个位置分别是L,R,那么这个数被计算了(pos-L)*(R-pos)次。考虑到每个数最大也只有10000,不由得想到去预处理。首先预处理出1到10000每个数的约数,然后,对于数列里某个数,枚举它的所有约数(最多64个),用vector记录每个约数出现在哪些位置上(即下标),再二分找到左边最近的和右边最近的位置,更新离它最近的约数的位置即可。这样只有log(n)复杂度。

代码:

#include <cstdio>  #include <iostream>  #include <cstring>  #include <string>  #include <cmath>  #include <algorithm>  #include <stack>  #include <vector>  #include <map>  #include <set>  using namespace std;    const int MAX = 1e5 + 5;  const int MOD = 1e9 + 7;  vector <int> yueshu[MAX], pos[MAX];    int n, a[MAX];    void initial() //预处理约数  {      for(int i = 0; i < MAX; i++)          yueshu[i].clear();      for(int i = 1; i <= 10000; i++)      {          for(int j = 1; j*j <= i; j++)          {              if(i%j == 0)              {                  yueshu[i].push_back(j);                  if(j*j != i)                      yueshu[i].push_back(i/j);              }          }      }  }    void input()  {      for(int i = 1; i <= n; i++)          scanf("%d", &a[i]);  }    void solve()  {      for(int i = 0; i < MAX; i++)          pos[i].clear();      for(int i = 1; i <= n; i++) //记录每个数出现的所有下标          pos[a[i]].push_back(i);      long long ans = 0;      for(int i = 1; i <= n; i++)      {          int maxl = 0, minr = n + 1;          for(int j = 0; j < yueshu[a[i]].size(); j++) //枚举约数          {              int temp = yueshu[a[i]][j];              if(pos[temp].size() == 0)                  continue;              if(pos[temp][0] > i)              {                  minr = min(minr, pos[temp][0]);                  continue;              }              if(pos[temp][pos[temp].size() - 1] < i)              {                  maxl = max(maxl, pos[temp][pos[temp].size() - 1]);                  continue;              }              int l = 0, r = pos[temp].size() - 1, mid;              while(l < r - 1) //二分找离a[i]左边和右边最近的当前约数              {                  mid = (l + r)/2;                  if(pos[temp][mid] < i)                      l = mid;                  else                      r = mid;              }              if(r + 1 < pos[temp].size()) //更新两边最近约数的位置                  minr = min(minr, pos[temp][r + 1]);              if(pos[temp][r] > i)                  minr = min(minr, pos[temp][r]);              if(pos[temp][l] < i)                  maxl = max(maxl, pos[temp][l]);          }          ans = (ans + (long long)(i - maxl)*(minr - i))%MOD;      }      printf("%I64d\n", ans);  }    int main()  {      initial();      while(scanf("%d", &n) != EOF)      {          input();          solve();      }      return 0;  }  


0 0
原创粉丝点击