HDU 5869.Different GCD Subarray Query-区间gcd+树状数组 (神奇的标记右移操作) (2016年ICPC大连网络赛)
树状数组。。。
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
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].
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
这个题的意思就是问区间内所有子段不同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大连网络赛)的更多相关文章
- HDU 5869 Different GCD Subarray Query rmq+离线+数状数组
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5869 Different GCD Subarray Query Time Limit: 6000/3 ...
- HDU 5997 rausen loves cakes(启发式合并 + 树状数组统计答案)
题目链接 rausen loves cakes 题意 给出一个序列和若干次修改和查询.修改为把序列中所有颜色为$x$的修改为$y$, 查询为询问当前$[x, y]$对应的区间中有多少连续颜色段. ...
- 51nod_1199 树的先跟遍历+区间更新树状数组
题目是中文,所以不讲题意 做法顺序如下: 使用先跟遍历,把整棵树平铺到一维平面中 使用自己整的区间更新树状数组模板进行相关操作. http://www.cnblogs.com/rikka/p/7359 ...
- 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 ( ...
- HDU 5654 xiaoxin and his watermelon candy 离线树状数组 区间不同数的个数
xiaoxin and his watermelon candy 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5654 Description Du ...
- 【HDU4947】GCD Array(莫比乌斯反演+树状数组)
点此看题面 大致题意: 一个长度为\(n\)的数组,实现两种操作:将满足\(gcd(i,k)=d\)的\(a_i\)加上\(v\),询问\(\sum_{i=1}^xa_i\). 对于修改操作的推式子 ...
- hdu 1556:Color the ball(第二类树状数组 —— 区间更新,点求和)
Color the ball Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- ACM学习历程—HDU5700 区间交(树状数组 && 前缀和 && 排序)
http://acm.hdu.edu.cn/showproblem.php?pid=5700 这是这次百度之星初赛2B的第五题.省赛回来看了一下,有这样一个思路:对于所有的区间排序,按左值排序. 然后 ...
- 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 ...
随机推荐
- Shuffle'm Up POJ - 3087(模拟)
Shuffle'm Up Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 15249 Accepted: 6962 Des ...
- linux 查看CPU内存 网络 流量 磁盘 IO
使用vmstat命令来察看系统资源情况 在命令行方式下,如何查看CPU.内存的使用情况,网络流量和磁盘I/O? Q: 在命令行方式下,如何查看CPU.内存的使用情况,网络流量和磁盘I/O? A: 在命 ...
- 二分法:CF371C-Hamburgers(二分法+字符串的处理)
Hamburgers Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Desc ...
- Kubernetes配置Ceph RBD StorageClass
1. 在Ceph上为Kubernetes创建一个存储池 # ceph osd pool create k8s 2. 创建k8s用户 # ceph auth get-or-create client.k ...
- vba中获取当前sheet页的名称,当前单元格所在位置
fname = ActiveSheet.Name-------获取当前sheet页的名称 Sname = "" & fname & "&qu ...
- ASP.NET Core 利用中间件支持跨域请求
方法1: 在Startup的ConfigureServices()中添加services.AddCors()在Startup的Configure()中添加app.UseCors(); 保证其在app. ...
- Linux内存使用消耗高
Linux系统下如果内存占用很高又找不到是被什么程序占用的,需要考虑下是否是SLAB的问题.SLAB是Linux操作系统的一种内存分配机制,可以使用下面命令来查看.例如: cat /proc/memi ...
- Spring MVC与jQuery结合使用Ajax技术
gradle配置 group 'org.zln.webDemo' version '1.0-SNAPSHOT' apply plugin: 'java' apply plugin: 'jetty' s ...
- cmake导出的visual studio工程带头文件
file(GLOB_RECURSE CURRENT_HEADERS *.h *.hpp) 分组显示 source_group("Include" FILES ${CURRENT_H ...
- proteus仿真 引脚显示电平变化但不能显示波形
proteus仿真 引脚显示电平变化但不能显示波形 原来是没有选择通道问题,proteus默认优先使用A通道才会显示波形,如果优先使用B,C,D通道,需要选择...