题目链接

题意

给定一个长度为 \(n\) 的数列 \(a_1,...,a_n\) 与 \(q\) 个询问 \(x_1,...,x_q\),对于每个 \(x_i\) 回答有多少对 \((l,r)\) 满足\(\ (1\leq l\leq r\leq n)\) 且 \(gcd(a_l,a_{l+1},...,a_r)=x_i\)

思路

对于固定的右端点 \(i\),将左端点从 (\(i\)) 向 (\(1\)) 延伸,\(gcd\) 值是递减的,且变化次数不超过 \(logC\) (\(C\)为数列中最大值)

下面讲述两种方法,第一种效率高一些,而第二种也提供了一些新的见解。

法一:滚动数组 —— 更新分段信息

枚举右端点,将由左端点划分出的 \(gcd\) 值分段。每次用新加进来的 \(a_i\) 去与刚刚的若干段再取 \(gcd\) 并更新分段信息,更新的同时统计数目。

保存与更新分段信息 可用滚动数组实现,统计数目 则显然用map(要注意的一点是:需要用map<int, LL>,因为数目可能会爆\(int\))。

法二:二分 + ST表 —— 找gcd值变化位置

参考自 hzwer.

如果说上一种做法是极大程度地利用了 上一次的信息,那么这一种做法就是抓住了 gcd值具有单调性 这个特点。

因此,确定分段位置可以直接采用二分查找,而如何快速地获取某一段的 \(gcd\) 值呢?就靠 \(ST\) 表大显身手了。

// 学到两点:

// 1. ST表适用的范围不仅局限于区间极值问题

// 2. 系统自带的log是真的慢...

Code

Ver. 1 : 171ms

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
typedef long long LL;
int a[maxn];
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
struct node { int x, p; };
map<int, LL> mp;
vector<node> v[2];
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
for (int i = 0; i < n; ++i) {
bool me = i & 1,
op = !me;
v[me].clear();
v[me].push_back({a[i], i});
int last = a[i];
for (auto nd : v[op]) {
int temp = gcd(nd.x, a[i]);
if (temp == last) v[me][v[me].size()-1].p = nd.p;
else v[me].push_back({temp, nd.p}), last = temp;
}
int now = i;
for (auto nd : v[me]) {
int pre = nd.p;
mp[nd.x] += now - pre + 1;
now = pre - 1;
}
}
int q, x;
scanf("%d", &q);
while (q--) {
scanf("%d", &x);
printf("%I64d\n", mp[x]);
}
return 0;
}

Ver. 2 : 296ms

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
typedef long long LL;
int gcd[maxn][32], a[maxn], n, Log[maxn], bin[32];
map<int, LL> mp;
int Gcd(int a, int b) { return b ? Gcd(b, a%b) : a; }
void rmqInit() {
Log[0] = -1; bin[0] = 1;
for (int i = 1; i < 20; ++i) bin[i] = bin[i-1] << 1;
for (int i = 1; i <= n; ++i) Log[i] = Log[i>>1] + 1, gcd[i][0] = a[i];
for (int j = 1; bin[j] <= n; ++j) {
for (int i = 1; i + bin[j-1] - 1 <= n; ++i) {
gcd[i][j] = Gcd(gcd[i][j-1], gcd[i + bin[j-1]][j-1]);
}
}
}
int query(int l, int r) {
int k = Log[r-l+1];
return Gcd(gcd[l][k], gcd[r-bin[k]+1][k]);
}
int bi(int i, int l, int r, int x) {
while (r-l>1) {
int mid = l+r >> 1, val = query(i, mid);
if (val >= x) l = mid;
else r = mid - 1;
}
return query(i, r) == x ? r : l;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
rmqInit();
for (int i = 1; i <= n; ++i) {
int l = i;
while (true) {
if (l == n+1) break;
int val = query(i, l);
int r = bi(i, l, n, val);
mp[val] += r-l+1;
l = r+1;
}
}
int q, x;
scanf("%d", &q);
while (q--) {
scanf("%d", &x);
printf("%I64d\n", mp[x]);
}
return 0;
}

Codeforces 475D CGCDSSQ 区间gcd值的更多相关文章

  1. Codeforces 475D CGCDSSQ 求序列中连续数字的GCD=K的对数

    题目链接:点击打开链接 #include <cstdio> #include <cstring> #include <algorithm> #include < ...

  2. codeforces 475D. CGCDSSQ

    D. CGCDSSQ time limit per test 2 seconds memory limit per test 256 megabytes Given a sequence of int ...

  3. Codeforces 475D CGCDSSQ(分治)

    题意:给你一个序列a[i],对于每个询问xi,求出有多少个(l,r)对使得gcd(al,al+1...ar)=xi. 表面上是询问,其实只要处理出每个可能的gcd有多少个就好了,当左端点固定的时候,随 ...

  4. Codeforces 475D 题解(二分查找+ST表)

    题面: 传送门:http://codeforces.com/problemset/problem/475/D Given a sequence of integers a1, -, an and q ...

  5. HDU 5726 GCD 区间GCD=k的个数

    GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submis ...

  6. 区间加值,区间gcd, 牛客949H

    牛客小白月赛16H 小阳的贝壳 题目链接 题意 维护一个数组,支持以下操作: 1: 区间加值 2: 询问区间相邻数差的绝对值的最大值 3: 询问区间gcd 题解 设原数组为\(a\), 用线段树维护\ ...

  7. Codeforces 914D - Bash and a Tough Math Puzzle 线段树,区间GCD

    题意: 两个操作, 单点修改 询问一段区间是否能在至多一次修改后,使得区间$GCD$等于$X$ 题解: 正确思路; 线段树维护区间$GCD$,查询$GCD$的时候记录一共访问了多少个$GCD$不被X整 ...

  8. Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论

    Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论 题意 给你一段数,然后小明去猜某一区间内的gcd,这里不一定是准确值,如果在这个区间内改变 ...

  9. FZU2224 An exciting GCD problem 区间gcd预处理+树状数组

    分析:(别人写的) 对于所有(l, r)区间,固定右区间,所有(li, r)一共最多只会有log个不同的gcd值, 可以nlogn预处理出所有不同的gcd区间,这样区间是nlogn个,然后对于询问离线 ...

随机推荐

  1. CentOS 7 bonding模式双网卡绑定

    1.1    查看系统内核是否支持bonding [root@cobbler-node1 ~]# cat /boot/config-3.10.0-693.el7.x86_64 | grep -i bo ...

  2. JZOJ 4725. 质数序列

    Description 由于去NOI的火车“堵”了数不清时间,小Z和小D打完ETG,闲着无聊开始看今年的JSOI省选题,并尝试着修改题目:对于一个长度为L ≥ 2的序列,X:x1,x2,...,xL ...

  3. JZOJ 4738. 神在夏至祭降下了神谕 DP + 线段树优化

    4738. 神在夏至祭降下了神谕 Time Limits: 1000 ms  Memory Limits: 262144 KB  Detailed Limits   Goto ProblemSet D ...

  4. Linux 用户行为日志记录

    工作中我们常常遇到,有的员工不安于被分配的权限,老是想sudo echo "ziji" /usr/bin/visudo NOPASSWD:ALL来进行提权,造成误删了数据库某条重要 ...

  5. Python抓取视频内容

    Python抓取视频内容 Python 是一种面向对象.解释型计算机程序设计语言,由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991年.Python语法简洁而清晰,具 ...

  6. MongoDB快速入门学习笔记4 MongoDB的文档查询操作

    先把student删除,再重新插入数据 > db.student.drop() true > db.student.insert([{ "_id" : 1, " ...

  7. Robotium接入到Jenkins持续集成自动化测试

    6.3 将测试用例接入到Jenkins 由于我是自己学习的手机自动化测试,没有实际投入到工作中使用,jenkins的接入也没有具体操作,现摘抄一下网页:http://www.tuicool.com/a ...

  8. 【LoadRunner】对摘要认证的处理

    近期项目中,进行http协议的接口性能测试过程中,需要进行登录接口的摘要认证,分享一下测试经验. 测试准备 测试工具:LoadRunner11 测试类型:接口测试--某系统登录接口 步骤 根据系统接口 ...

  9. 《HTTP协议详解》读书笔记---请求篇之情求方法

    之前对于网络这一块不是很清楚,值知道TCP/IP协议,三次握手四次握手之类的很笼统,零碎的知识,现在打算系统学习下网络相关的知识,先从http协议开始. 本人,还是新手,对于一些知识如果有理解错误的, ...

  10. python 学习分享-迭代器与生成器篇

    迭代器 迭代是Python最强大的功能之一,是访问集合元素的一种方式.. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后 ...