Hdu 4777 Rabbit Kingdom (树状数组)

题目链接:

  Hdu 4777  Rabbit Kingdom

题目描述:

  有一个含有n个数的序列,m个询问。问 [l, r] 区间内与所有数都互质的数有几个?

解题思路:

  先预处理出来每个数的贡献区间,每个数的贡献区间是 [左边最近不互质数的位置,右边最近不互质数的位置] ,现在问题就转化为了求区间 [L, R] 中有几个数的贡献区间能完整覆盖 [L, R] 区间。但是数据范围挺大的,可以考虑用树状数组离线处理来达到优化的目的。先对所有的查询进行排序 (按照L升序排列) ,然后从左到右依次处理。我们用树状数组区间和sum [L, R] 表示区间[L, R]中符合题意的有多少个数。假设现在需要查询 [L, R] 区间,我们可以考虑贡献区间L[i]<L的数,可以在i位置加 1,然后在R[i]位置减1。这样处理的话必须要保证L[i]小于查询区间L的值,以及i要在查询区间内。所以我们每次向后移动的时候,要在树状数组中减去当前位置的数对树状数组的影响,也就是进行操作 i 位置减1,R[i]位置加1. 分析到这里这个题目就变成了一个比较水的利用树状数组进行点更新,区间查询的题目了,也可以用线段树搞的。

  1 #include <vector>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <iostream>
  5 #include <algorithm>
  6 using namespace std;
  7 
  8 const int maxn = 200010;
  9 struct node
 10 {
 11     int l, r, num;
 12 }q[maxn];
 13 int tree[maxn], a[maxn], vis[maxn];
 14 int ans[maxn], L[maxn], R[maxn];
 15 
 16 vector <int> vec[maxn];
 17 vector <int> p[maxn];
 18 bool cmp (node a, node b)
 19 {
 20     return a.l < b.l;
 21 }
 22 //预处理每个数的因子
 23 void init ()
 24 {
 25     for (int i=0; i<maxn; i++)
 26         p[i].clear();
 27     for (int i=2; i<maxn; i++)
 28         for (int j=i; j<maxn; j+=i)
 29             p[j].push_back(i);
 30 }
 31 //树状数组
 32 int lowbit (int x)
 33 {
 34     return x & (-x);
 35 }
 36 void add (int x, int val)
 37 {
 38     for (int i=x; i<maxn; i+=lowbit(i))
 39         tree[i] += val;
 40 }
 41 int Sum (int x)
 42 {
 43     int sum = 0;
 44     while (x)
 45     {
 46         sum += tree[x];
 47         x -= lowbit(x);
 48     }
 49     return sum;
 50 }
 51 
 52 int main ()
 53 {
 54     int n, Q;
 55     init ();
 56     while (scanf ("%d %d", &n, &Q), n + Q)
 57     {
 58         for (int i=1; i<=n; i++)
 59             scanf ("%d", &a[i]);
 60         for (int i=0; i<Q; i++)
 61         {
 62             scanf ("%d %d", &q[i].l, &q[i].r);
 63             q[i].num = i;
 64         }
 65 
 66         sort (q, q+Q, cmp);
 67         memset (vis, 0, sizeof(vis));
 68         //预处理每个数的左边界
 69         for (int i=1; i<=n; i++)
 70         {
 71             L[i] = 0;
 72             for (int j=0; j<p[a[i]].size(); j++)
 73             {
 74                 int x = p[a[i]][j];
 75                 L[i] = max (L[i], vis[x]);
 76                 vis[x] = i;
 77             }
 78         }
 79         //预处理每个数的右边界
 80         for (int i=0; i<=maxn; i++)
 81         {
 82             vec[i].clear();
 83             vis[i] = n + 1;
 84         }
 85         for (int i=n; i>0; i--)
 86         {
 87             R[i] = n + 1;
 88             for (int j=0; j<p[a[i]].size(); j++)
 89             {
 90                 int x = p[a[i]][j];
 91                 R[i] = min (R[i], vis[x]);
 92                 vis[x] = i;
 93             }
 94         }
 95 
 96         memset (tree, 0, sizeof(tree));
 97         for (int i=1; i<=n; i++)
 98         {
 99             if (L[i])
100                 vec[L[i]].push_back(i);
101             else
102             {
103                 add (i, 1);
104                 if (R[i] <= n)
105                     add (R[i], -1);
106             }
107         }
108         for (int i=1, j=0; i<=n; i++)
109         {
110             while (q[j].l == i && j < Q)
111             {
112                 ans[q[j].num] = Sum(q[j].r) - Sum(q[j].l-1);
113                 j ++;
114             }
115             add (i, -1);
116             if (R[i] <= n)
117                 add (R[i], 1);
118             for (int k=0; k<vec[i].size(); k++)
119             {
120                 int x = vec[i][k];
121                 add (x, 1);
122                 add (R[x], -1);
123             }
124         }
125         for (int i=0; i<Q; i++)
126             printf ("%d
", ans[i]);
127     }
128     return 0;
129 }