树状数组。。。

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
 

这个题的意思就是问区间内所有子段不同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的最大右端点。直接查询就可以。

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

代码:

 //离线处理-树状数组
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<vector>
#include<utility>
using namespace std;
const int maxn=1e5+;
int Arr[maxn];//存数据一开始的值
int N,Q;
int pos[maxn*]; //记录gcd的位置
vector<pair<int ,int> >querys[maxn];
vector<pair<int, int> >gcds[maxn];
int ans[maxn];
int treeArr[maxn]; //树状数组 int gcd(int a,int b){ //gcd求最大公约数
if(!(a%b))return b;
else return gcd(b,a%b);
} void init(){ //初始化
int tmp,ts;
for(int i=;i<=N;i++){
tmp=Arr[i];
ts=i; //固定右端点,向左遍历
for(int j=;j<gcds[i-].size();j++){
int tmpgcd=gcd(tmp,gcds[i-][j].first);
if(tmpgcd!=tmp){ //如果gcd发生变化
gcds[i].push_back(make_pair(tmp,ts)); //first存gcd,second存gcd的左端点ts
ts=gcds[i-][j].second; //上一个gcd的右端点就是下一个gcd的左端点
tmp=tmpgcd;
}
}
gcds[i].push_back(make_pair(tmp,ts)); //将与上一个的最左端的gcd存起来
}
return;
} int lowbit(int x){ //取最低位的1
return x&(-x);
} void Add(int cur,int num){ //单点更新
for(int i=cur;i<=N;i+=lowbit(i))
treeArr[i]+=num; //由叶子节点向上更新树状数组
return;
} int Query(int cur){ //区间查询 从右端点往左加二进制最低位1的
int sum=;
for(int i=cur;i>;i-=lowbit(i))
sum+=treeArr[i];
return sum;
} void Solve(){
memset(pos,,sizeof(pos));
memset(treeArr,,sizeof(treeArr));
for(int i=;i<=N;i++){
for(int j=;j<gcds[i].size();j++){
if(pos[gcds[i][j].first]){ //如果标记已经存在,就将标记去掉,所以执行单点更新操作
Add(pos[gcds[i][j].first],-);
}
Add(gcds[i][j].second,);//一个新的不同的gcd,从叶子节点开始更新个数+1
pos[gcds[i][j].first]=gcds[i][j].second;//记录该gcd的右端点
}
for(int j=;j<querys[i].size();j++){
ans[querys[i][j].second]=Query(i)-Query(querys[i][j].first-);//区间右端点-区间左区间
}
}
for(int i=;i<=Q;i++)
printf("%d\n",ans[i]); return;
}
int main(){
//reopen("input","r",stdin);
while(~scanf("%d%d",&N,&Q)){
for(int i=;i<=N;i++){
scanf("%d",&Arr[i]); //将数据存在Arr数组中
querys[i].clear(); //清空
gcds[i].clear(); //清空
}
init();
int tmp1,tmp2;
for(int i=;i<=Q;i++){
scanf("%d%d",&tmp1,&tmp2);
querys[tmp2].push_back(make_pair(tmp1,i)); //将tmp1与i成对插入vector的tmp2下标里
}
Solve();
}
return ;
}

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

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

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

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

  1. HDU 5869 Different GCD Subarray Query rmq+离线+数状数组

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5869 Different GCD Subarray Query Time Limit: 6000/3 ...

  2. HDU 5997 rausen loves cakes(启发式合并 + 树状数组统计答案)

    题目链接  rausen loves cakes 题意  给出一个序列和若干次修改和查询.修改为把序列中所有颜色为$x$的修改为$y$, 查询为询问当前$[x, y]$对应的区间中有多少连续颜色段. ...

  3. 51nod_1199 树的先跟遍历+区间更新树状数组

    题目是中文,所以不讲题意 做法顺序如下: 使用先跟遍历,把整棵树平铺到一维平面中 使用自己整的区间更新树状数组模板进行相关操作. http://www.cnblogs.com/rikka/p/7359 ...

  4. hdu 5869 Different GCD Subarray Query BIT+GCD 2016ICPC 大连网络赛

    Different GCD Subarray Query Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K ( ...

  5. HDU 5654 xiaoxin and his watermelon candy 离线树状数组 区间不同数的个数

    xiaoxin and his watermelon candy 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5654 Description Du ...

  6. 【HDU4947】GCD Array(莫比乌斯反演+树状数组)

    点此看题面 大致题意: 一个长度为\(n\)的数组,实现两种操作:将满足\(gcd(i,k)=d\)的\(a_i\)加上\(v\),询问\(\sum_{i=1}^xa_i\). 对于修改操作的推式子 ...

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

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

  8. ACM学习历程—HDU5700 区间交(树状数组 && 前缀和 && 排序)

    http://acm.hdu.edu.cn/showproblem.php?pid=5700 这是这次百度之星初赛2B的第五题.省赛回来看了一下,有这样一个思路:对于所有的区间排序,按左值排序. 然后 ...

  9. POJ 3321:Apple Tree + HDU 3887:Counting Offspring(DFS序+树状数组)

    http://poj.org/problem?id=3321 http://acm.hdu.edu.cn/showproblem.php?pid=3887 POJ 3321: 题意:给出一棵根节点为1 ...

随机推荐

  1. 各种友(e)善(xin)数论总集(未完待续),从入门到绝望

    目录 快速幂 扩展欧几里得 GCD 扩展欧几里得 同余系列 同余方程 同余方程组 一点想法 高次同余方程 BSGS exBSGS 线性筛素数 埃式筛 欧拉筛 欧拉函数 讲解 两道水题 法雷级数 可见点 ...

  2. Linux安全层详解

    1 bastion(安全堡垒系统)通常配置两个服务: 1 服务定义系统的功能: 2 服务支持远程访问: 原则: 1 不需要某个软件卸载掉: 2 需要某个软件但不使用就不要激活: 为各个bastion系 ...

  3. [Noip2016]换教室(期望+DP)

    Description 题目链接:Luogu Solution 这题结合了DP和概率与期望,其实只要稍微知道什么是期望就可以了, 状态的构造很关键,\(F[i][j][0/1]\)表示已经到第\(i\ ...

  4. STL 里面的几个容器简叙

    出处:http://blog.csdn.net/niushuai666/article/details/6654951 list1.list的成员函数push_back()把一个对象放到一个list的 ...

  5. 使用android-junit-report.jar导出单元测试报告

    Android在使用脚本编译和测试时,使用默认的testrunner不会输出文件类型的单元测试报告,每次只能分析logcat的无法直观的看到单元测试结果和报告,这给编写自动化脚本带来了不少麻烦,虽然可 ...

  6. Docker Hadoop LAMP安装配置教程

    以下教程来自九章算法. 1.How to install Dockerhttps://bupt.quip.com/YehSAR4qnGqB 2.How to set up hadoop environ ...

  7. CentOS6.5 安装中文输入法

    直接转载他人文章:http://hi.baidu.com/dxqt58592/item/a85b96e72c423cc0baf37d3c centos 6.5使用yum安装中文输入法 1.需要root ...

  8. zookeeper 集群

    集群步骤: 1.安装zookeeper 2.修改zookeeper配置文件 3.创建myid文件 安装zookeeper:查看安装步骤 修改zookeeper配置文件:在zoo.cfg中添加配置 se ...

  9. 基础概念:Oracle数据库

    基础概念:Oracle数据库.实例.用户.表空间.表之间的关系 数据库:Oracle数据库是数据的物理存储.这就包括(数据文件ORA或者DBF.控制文件.联机日志.参数文件).其实Oracle数据库的 ...

  10. java-dispose方法

    今天在编一道JAVA例题时,看到 dispose 这个方法,在程序中是用来关闭一个GUI页面的.这也让我想到了setVisible(false)方法.在网上查了意思,dispose()即释放所有本机屏 ...