树状数组。。。

Different GCD Subarray Query

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1541    Accepted Submission(s): 599

Problem Description
This is a simple problem. The teacher gives Bob a list of problems about GCD (Greatest Common Divisor). After studying some of them, Bob thinks that GCD is so interesting. One day, he comes up with a new problem about GCD. Easy as it looks, Bob cannot figure it out himself. Now he turns to you for help, and here is the problem:
  
  Given an array a of N positive integers a1,a2,⋯aN−1,aN; a subarray of a is defined as a continuous interval between a1 and aN. In other words, ai,ai+1,⋯,aj−1,aj is a subarray of a, for 1≤i≤j≤N. For a query in the form (L,R), tell the number of different GCDs contributed by all subarrays of the interval [L,R].
  
Input
There are several tests, process till the end of input.
  
  For each test, the first line consists of two integers N and Q, denoting the length of the array and the number of queries, respectively. N positive integers are listed in the second line, followed by Q lines each containing two integers L,R for a query.

You can assume that 
  
    1≤N,Q≤100000 
    
   1≤ai≤1000000

Output
For each query, output the answer in one line.
Sample Input
5 3
1 3 4 6 9
3 5
2 5
1 5

Sample Output
6
6
6

Source
2016 ACM/ICPC Asia Regional Dalian Online

这个题的意思就是问区间内所有子段不同gcd的个数。

一开始理解错了题意,后来想明白了。假设区间的数为1 2 3 4

就是gcd(1),gcd(2),gcd(3),gcd(4),gcd(1,2),gcd(2,3),gcd(3,4),gcd(1,2,3),gcd(2,3,4),gcd(1,2,3,4)这些里面不同的gcd的个数是多少。

如果用在线算法操作,每查询一次就要处理依次数据,一方面会造成浪费,另一方面,你这样写妥妥的超时啊,所以要用离线算法,将所有的数据处理好之后,按顺序直接输出结果就可以。

首先用一个数组将数据保存起来,然后用一个vector数组将查询的区间和查询的顺序记录下来。

处理数据,就是相对来说固定右端点,从右往左移,在代码里就是对于每一个i(固定右端点),遍历一下i所在的子段的gcd,因为i不变,j是上一个i的gcd的值保存的顺序,j越大,i所在的子段就依次向左增加一个数,如果gcd发生了变化,就记录一下这个gcd以及出现的位置。可能越说越乱,画一个图表示一下。。。

图的意思就是假设数据为2 4 9 6 5,i为下标。假设i为4,就是固定4,然后遍历j,j存的是上一个i的子段的gcd,通过上一个i的gcd来得到i为4时(6)的子段的gcd,图中画的横线的长度就是子段的长度,橙色的矩形包含的长度是上一个i处理出的数据。就是固定右端点,依次往左遍历得到所有的gcd,这样不会重复操作,也不会漏掉某个子段。

将gcd整理出来之后,怎么操作才能使得区间查询的是不同的gcd的个数呢?

对于区间内相同的gcd,让标记gcd的下标尽量右移,找该gcd的最大右端点。

通过树状数组维护gcd的下标。

一边维护,一边查询树状数组就可以保证数据正确。

总结一下就是:固定右端点,依次往左找出来所有的gcd,并标记下标,因为是i逐渐增大的,所以后面遍历的时候也是相同gcd的最大右端点。直接查询就可以。

就这样吧,不想好好写了。

代码:

 1 //离线处理-树状数组
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<cstdlib>
 7 #include<algorithm>
 8 #include<queue>
 9 #include<vector>
10 #include<utility>
11 using namespace std;
12 const int maxn=1e5+10;
13 int Arr[maxn];//存数据一开始的值
14 int N,Q;
15 int pos[maxn*10]; //记录gcd的位置
16 vector<pair<int ,int> >querys[maxn];
17 vector<pair<int, int> >gcds[maxn];
18 int ans[maxn];
19 int treeArr[maxn]; //树状数组
20
21 int gcd(int a,int b){ //gcd求最大公约数
22     if(!(a%b))return b;
23     else return gcd(b,a%b);
24 }
25
26 void init(){ //初始化
27     int tmp,ts;
28     for(int i=1;i<=N;i++){
29         tmp=Arr[i];
30         ts=i;                                     //固定右端点,向左遍历
31         for(int j=0;j<gcds[i-1].size();j++){
32             int tmpgcd=gcd(tmp,gcds[i-1][j].first);
33             if(tmpgcd!=tmp){  //如果gcd发生变化
34                 gcds[i].push_back(make_pair(tmp,ts)); //first存gcd,second存gcd的左端点ts
35                 ts=gcds[i-1][j].second; //上一个gcd的右端点就是下一个gcd的左端点
36                 tmp=tmpgcd;
37             }
38         }
39         gcds[i].push_back(make_pair(tmp,ts)); //将与上一个的最左端的gcd存起来
40     }
41     return;
42 }
43
44 int lowbit(int x){  //取最低位的1
45     return x&(-x);
46 }
47
48 void Add(int cur,int num){ //单点更新
49     for(int i=cur;i<=N;i+=lowbit(i))
50         treeArr[i]+=num;   //由叶子节点向上更新树状数组
51     return;
52 }
53
54 int Query(int cur){ //区间查询  从右端点往左加二进制最低位1的
55     int sum=0;
56     for(int i=cur;i>0;i-=lowbit(i))
57         sum+=treeArr[i];
58     return sum;
59 }
60
61 void Solve(){
62     memset(pos,0,sizeof(pos));
63     memset(treeArr,0,sizeof(treeArr));
64     for(int i=1;i<=N;i++){
65         for(int j=0;j<gcds[i].size();j++){
66             if(pos[gcds[i][j].first]){ //如果标记已经存在,就将标记去掉,所以执行单点更新操作
67                 Add(pos[gcds[i][j].first],-1);
68             }
69             Add(gcds[i][j].second,1);//一个新的不同的gcd,从叶子节点开始更新个数+1
70             pos[gcds[i][j].first]=gcds[i][j].second;//记录该gcd的右端点
71         }
72         for(int j=0;j<querys[i].size();j++){
73             ans[querys[i][j].second]=Query(i)-Query(querys[i][j].first-1);//区间右端点-区间左区间
74         }
75     }
76     for(int i=1;i<=Q;i++)
77         printf("%d\n",ans[i]);
78
79     return;
80 }
81 int main(){
82     //reopen("input","r",stdin);
83     while(~scanf("%d%d",&N,&Q)){
84         for(int i=1;i<=N;i++){
85             scanf("%d",&Arr[i]); //将数据存在Arr数组中
86             querys[i].clear(); //清空
87             gcds[i].clear(); //清空
88         }
89         init();
90         int tmp1,tmp2;
91         for(int i=1;i<=Q;i++){
92             scanf("%d%d",&tmp1,&tmp2);
93             querys[tmp2].push_back(make_pair(tmp1,i)); //将tmp1与i成对插入vector的tmp2下标里
94         }
95         Solve();
96     }
97     return 0;
98 }

就这样吧,这题没什么,主要是想明白就很简单。

咸鱼太菜,学长要捶爆我的狗头吗???

已经做好了被学长打死的思想准备。。。

转载于:https://www.cnblogs.com/ZERO-/p/8672921.html

HDU 5869.Different GCD Subarray Query-区间gcd+树状数组 (神奇的标记右移操作) (2016年ICPC大连网络赛)...相关推荐

  1. HDU - 5877 Weak Pair 2016 ACM/ICPC 大连网络赛 J题 dfs+树状数组+离散化

    题目链接 You are given a rootedrooted tree of NN nodes, labeled from 1 to NN. To the iith node a non-neg ...

  2. HDU - 5875 2016 ACM/ICPC 大连网络赛 H题 暴力

    题目链接 题意:给你一个区间l,r一直将val[l]模上val[l+1],val[l+2]...val[r],因为一个模上比前一个数小数是没有意义的,所以需要将每一个点找到右边第一个小于他的点就行. ...

  3. HDU - 5876 Sparse Graph 2016 ACM/ICPC 大连网络赛 I题 bfs+set+补图最短路

    题目链接 题意:给的补图,让你求一个源点到其他点的最短距离,因为图太稠密了, 用dij以及spfa根本不得行,这里只能用一种我不会方法来进行,这里用了bfs的方法以及set来维护,分别set维护一个未 ...

  4. hdu 1556:Color the ball(第二类树状数组 —— 区间更新,点求和)

    Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  5. HDU 5465 Clarke and puzzle (二维树状数组维护区间异或)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5465 解题思路: 因为要对大量的区间进行异或,所以考虑用二维树状数组就行维护. #include< ...

  6. HDU多校1 - 6959 zoto(莫队+树状数组/值域分块)

    题目链接:点击查看 题目大意:在二维平面内有 nnn 个点,表示为 (i,f[i])(i,f[i])(i,f[i]),需要回答 mmm 次询问,每次询问会给出一个矩形,问矩形内有多少个不同的 yyy ...

  7. hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询

    点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...

  8. hdu 5465 Clarke and puzzle (二维树状数组+nim博弈)

    解析: 利用二维树状数组来区间询问异或和,以及单点更新,然后利用nim博弈的结论判断胜负. mymy codecode #include <cstdio> #include <cst ...

  9. hdu 4001 (2011ACM/ICPC大连网络赛)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4001 题意不多解释,说说思路:按照长和宽排序,排序后,前面的方块一定不能叠放在后面的方块上,而后面的方块有 ...

最新文章

  1. DWZ与KindEditor编辑器的整合
  2. LINUX 使用tcgetattr函数与tcsetattr函数控制终端五
  3. 中国汽车电器运营现状与十四五发展规划报告2022-2028年版
  4. MRU 列表序列化的 Boost.MultiIndex 示例
  5. rm命令改为移动到回收站
  6. 注入点批量收集工具_原来微信群也是能够批量管理的,学到了
  7. 为什么MediaPlayer中onCompletion()每次播放音频时都触发?
  8. python互斥锁_python互斥锁
  9. WPF教程六:布局之Grid面板(转)
  10. 【Windows Phone设计与用户体验】关于移动产品的Loading用户体验的思考
  11. 蓝桥杯 ADV-234 算法提高 字符串跳步
  12. 豆瓣python网络数据采集器代理_Python 网络数据采集1
  13. 51nod 1833 状压dp加一点图论
  14. 单片机步进电机正反转C语言程序,单片机控制步进电机正反转
  15. IE9 和 IE11 安装及相关补丁
  16. 3D Max2018安装教程
  17. jquery版本共存_多个jQuery版本共存的处理方案
  18. php居中显示代码,css居中代码是什么
  19. 大数据查询怎么优化?
  20. 抢购软件使用方法(如何开发抢购软件)

热门文章

  1. bootloader启动流程分析
  2. net.conn read 判断数据读取完毕_高并发:缓存模式以及缓存的数据一致性
  3. vk_down 每次下翻丙行 c++_笔记本接口不够用?不妨试试这款Type-C拓展坞,给你7个接口用...
  4. 三、解决ie缓存问题
  5. TensorFlow 2.0 - tf.saved_model.save 模型导出
  6. LeetCode MySQL 1280. 学生们参加各科测试的次数
  7. LeetCode 57. 插入区间(一次遍历)
  8. LeetCode 217. 存在重复元素(哈希)
  9. 安卓手机主题软件_类似主题软件下载-类似主题安卓官方版下载v2.6.6.3
  10. Codeforces Round #697 (Div. 3)A~G解题报告