HDU 5869 Different GCD Subarray Query 树状数组 + 一些数学背景
http://acm.hdu.edu.cn/showproblem.php?pid=5869
题意:给定一个数组,然后给出若干个询问,询问[L, R]中,有多少个子数组的gcd是不同的。
就是[L, R]中不同区间的gcd值,有多少个是不同的。
给个样例
3 3
7 7 7
1 2
1 3
3 3
数学背景:
一个数字和若N个数字不断GCD,其结果只有loga[i]种,为什么呢?因为可以把a[i]质因数分解,其数目最多是loga[i]个数字相乘。(最小的数字是2,那么loga[i]个2相乘也爆了a[i]了)
所以,考虑以a[i]为结尾的序列,有多少个不同的gcd,可以记录下。
比如样例。括号里的依次是gcd值和a[i]这个数字最早与那个位置gcd,其值是val、
1、 3、 4、 6、 9
(1, 1) (3, 2) (4, 3) (6, 4) (9, 5)
(1, 1) (1, 2) (2, 3) (3, 4)
(1, 2) (1, 3) //9这个数字在[3, 5]中一路gcd,就能得到1
这里其实已经维护了左端点了,要求左端点尽量大。
1、为什么要维护左端点,因为答案给出的是[L, R]这段区间的不同gcd,如果你预处理的时候,R和L - 1得到的gcd是不能算进去的。
2、要求左端点尽量大,那是因为我后来要对R排序,尽量往R靠,结果是最优的,(可以参考下求区间不同数字个数的方法。后面再写。)
然后就是套路了。把得到的gcd往左端点里塞。♥,这是重点,一开始的时候我把不同的gcd往右端点塞了,这是不对的,因为往又端点塞的话,R与L - 1的gcd就会被你算进去了,所以会wa。
book[x]表示x这个值出现的最右的那个位置。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL;#include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> const int maxn = 1000000 + 20; const int N = 1000000 + 20; int c[maxn]; int lowbit(int x) {return x & (-x); } void upDate(int pos, int val) {while (pos <= N - 20) {c[pos] += val;pos += lowbit(pos);} } int query(int pos) {int ans = 0;while (pos) {ans += c[pos];pos -= lowbit(pos);}return ans; } int n, q; int a[maxn]; struct node {int L, R;int id;bool operator < (const struct node & rhs) const {return R < rhs.R;} }b[maxn]; int book[maxn]; int ans[maxn]; void work() {memset(book, 0, sizeof book);memset(c, 0, sizeof c);for (int i = 1; i <= n; ++i) {scanf("%d", &a[i]);}for (int i = 1; i <= q; ++i) {scanf("%d%d", &b[i].L, &b[i].R);if (b[i].L > b[i].R) swap(b[i].L, b[i].R);b[i].id = i;}sort(b + 1, b + 1 + q);int cur = 1; // for (int i = 1; i <= q; ++i) { // cout << b[i].L << " " << b[i].R << endl; // }for (int i = 1; i <= q; ++i) {for (int j = cur; j <= b[i].R; ++j) {int x = a[cur];for (int k = cur; k >= 1; k--) {if (book[x] < k) { //左端点只能尽量右靠,左边的不管,样例:7 7 7if (book[x]) { //树状数组不能从0开始upDate(book[x], -1);}book[x] = k; //不同的gcd,压到左端点upDate(book[x], 1);} // if (book[x] > k) while(1); // book[x] = k; // upDate(book[x], 1);if (k == 1) break;if (x == 1) break;x = __gcd(x, a[k - 1]);}cur++;}ans[b[i].id] = query(b[i].R) - query(b[i].L - 1);}for (int i = 1; i <= q; ++i) {printf("%d\n", ans[i]);}} int main() { #ifdef localfreopen("data.txt","r",stdin); #endifwhile (scanf("%d%d", &n, &q) != EOF) work();return 0; }
View Code
关于树状数组
3.1、求逆序对:首先先把数据离散化,因为树状数组覆盖的区间是1—max这样的,不离散的话开不到那么大的数组。例如离散后是:5、2、1、4、3、思路是:插入5,add(5,1),把pos为5的地方设置为1,然后ans += i - get_sum(5); i的意思是当前插入了i个数,然后get_sum()是当前有多少个数比5少,其实就是问1—5之间存在多少个数,那当然是比5小的啦。一减,就是关于5逆序对个数。eg:关于2的逆序对个数是1对。
LL get_inversion (int a[],int lena) //求逆序对个数
{
LL ans = 0;//逆序对一般都很多,需要用LL
for (int i=1;i<=lena;++i) // a[]={5,2,1,4,3} ans=6;
{ // a[]={5,5,5,5,5} ans=0; 逆序对严格大于
add(a[i],1); ans += i-get_sum(a[i]);
}
return ans;
}
关于数据离散化,可以开一个结构体,保存val和pos,然后根据val排序一下,根据pos从小到大赋值即可。for (int i=1;i<=n;++i) a[book[i].pos]=i; //从小到大离散。a[3]=1,a[1]=2等等
{9,1,0,5,4} 离散化后 {5,2,1,4,3}
3.2、求解区间不同元素个数,离线算法。复杂度O(q + nlog(n))
设树状数组的意义是:1--pos这个段区间的不同元素的种类数。怎么做?就是add(pos,1);在这个位置中+1,就是说这个位置上元素种类+1。然后先把询问按R递增的顺序排序。因为这里是最优的,我每次尽量往R靠,使得查询不重不漏。什么意思呢?就是假如有:2、1、3、5、1、7的话。一开始的[1,4]这段数字全部压进树状数组,用个数组book[val],表示val这个元素出现的最右的位置,因为我们需要删除重复的,也是要尽量往右靠。到达pos=5这个位置的时候,注意了,因为1是出现过的book[1] = 2,所以我们要做的是把2这个位置出现元素的种类数-1,就是add(book[1], -1)。然后把第五个位置出现的元素种类数+1,就是add(5,1)。为什么呢?因为你尽量把种类往右靠,因为我们的R是递增的,这样,你使得查询[4,6]成为可能,因为我那个1加入来了,而不是一直用pos=2那个位置的1,再者,查询[4,7]的话,一样的意思,因为中间的1进来了。所以我们因为尽量往右靠,毕竟我们都把query按R排序了。还有这个只能离线,一直预处理ans[i]表示第i个询问的ans。更新到[4,7]后,查询[1,2]已经不可能了,因为很明显,pos=2这个位置已经被删除了。
void work ()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
int q; scanf("%d",&q);
for (int i=1;i<=q;++i)
{
scanf("%d%d",&query[i].L,&query[i].R);
query[i].id = i; //记录ans
}
sort(query+1,query+1+q);
int cur = 1;
for (int i=1;i<=q;++i)
{
for (int j=cur;j<=query[i].R;++j)
{
if (book[a[j]])
add(book[a[j]],-1); //del 这个位置
book[a[j]]=j; //更新这个位置的最右值
add(j,1); //这个位置出现了新元素
}
cur = query[i].R+1; //表示现在预处理到这个位置了。不能往回查,而且也不会往回
ans[query[i].id] = get_sum(query[i].R) - get_sum(query[i].L-1); //区间减法
}
for (int i=1;i<=q;++i)
printf ("%d\n",ans[i]);
}
转载于:https://www.cnblogs.com/liuweimingcprogram/p/6050569.html
HDU 5869 Different GCD Subarray Query 树状数组 + 一些数学背景相关推荐
- HDU 5869.Different GCD Subarray Query-区间gcd+树状数组 (神奇的标记右移操作) (2016年ICPC大连网络赛)...
树状数组... Different GCD Subarray Query Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/6 ...
- HDU 6447 YJJ's Salesman(树状数组优化DP + 离散化)
HDU 6447 YJJ's Salesman 题目 给一个二维数组,从(0,0)走到(1e9, 1e9).每次只能走右,下,右下,三个方向.其中只有通过右下走到特定给出的点(村庄)时才会获得分值.问 ...
- 【 HDU 1166】 敌兵布阵 树状数组从0到1
如果给你一个数组,让你求某个区间的和,你很自然会想到遍历一遍数组,复杂度是O(n),但是如果有多次询问呢,你也许会想到用前缀数组,通过O(n)的预处理,达到O(1)的查询,但是如果要更新某个元素的值呢 ...
- HDU 5792 World is Exploding (树状数组)
World is Exploding 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5792 Description Given a sequence ...
- HDU 1166 敌兵布阵【树状数组】
用树状数组很简单,太晚了,贴下代码睡觉去... 另,研究线段树的时候,发现网上流传着有几种不同的线段树,最正宗的是以单位区间为单位,只能处理线段:另外还有几种叶子结点是点的,这种也可以用来处理点,所以 ...
- HDU 4630 No Pain No Game 树状数组+离线操作
题意:给一串数字,每次查询[L,R]中两个数的gcd的最大值. 解法:容易知道,要使取两个数让gcd最大,这两个数最好是倍数关系,所以处理出每个数的所有倍数,两两间根据倍数关系形成一条线段,值为该数. ...
- HDU 5792 World is Exploding(树状数组+离散化)
http://acm.split.hdu.edu.cn/showproblem.php?pid=5792 题意: 思路: lmin[i]:表示左边比第i个数小的个数. lmax[i]:表示左边比第i个 ...
- hdu 4911 求逆序对数+树状数组
http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能 ...
- hdu 1166 敌兵布阵(树状数组)
题意:区间和 思路:树状数组 #include<iostream> #include<stdio.h> #include<string.h> using names ...
最新文章
- GPUImageMovieWriter实现暂停录制,重新录制
- 计算机网络:第三章 数据链路层
- SiameseRPN++分析
- 【matplotlib】远程服务器使用报错 $DISPLAY
- 第一个SpringBoot应用
- Django update用法
- 深入理解 Java 线程池!
- Spark的这些事lt;二gt;——几个概念
- P3802 小魔女帕琪
- maven夹包引入的速度问题
- kuangbin专题
- asymptotic notation and recursion
- YOLOv5、v7改进之三十七:结合CVPR2022新作ConvNeXt网络
- 智能家居蓝海 如何才能破解“外热内冷”的尴尬?
- 技术书籍也香艳【关于Head First Design Patterns 封面女郎】
- Word文档 | 删除回车符
- 【R语言】5种探索数据分布的可视化技术
- int64_t 在 32 位环境下其实是 long long
- 广告计算学 刘鹏 听课笔记
- Android 深入探讨sigleTask和singletop的区别
热门文章
- SAP Fiori应用Footerbar区域按钮的高亮显示逻辑
- 加拿大文件服务器,加拿大服务器空间
- springboot+jsp中文乱码_【spring 国际化】springMVC、springboot国际化处理详解
- android gps 锁屏更新坐标_MIUI内测版更新日志解析,以及动画解说!
- MAVEN [ERROR] 不再支持源选项 5。请使用 7 或更高版本。
- 大二上学期总结计算机专业,计算机学生的大二第二学期自我总结-自我总结
- html5 页面 参数传递,详解html中页面跳转传递参数的问题
- android rfid 读写sdk,Android-SDK-1.0.0-STD android手机调用RFID模块读取电子标签Demo - 下载 - 搜珍网...
- 片偏移字段的值怎么算_搞懂钢丝网片计算原理,怎么算都不怕出错!
- python soup提取叶子标签_python 利用beautifulSoup提取页面多个标签的文本内容