题面:

传送门:http://codeforces.com/problemset/problem/475/D

Given a sequence of integers a1, …, an and q queries x1, …, xq on it. For each query xi you have to count the number of pairs (l, r) such that 1 ≤ l ≤ r ≤ n and gcd(al, al + 1, …, ar) = xi.

题目大意:

对于n个数的序列,给出q个询问,问有多少个区间满足区间最大公约数为x

分析:

求区间最大公约数很简单,可以利用Sparse-Table算法O(nlog2n)" role="presentation" style="position: relative;">O(nlog2n)O(nlog2n)预处理,O(1)" role="presentation" style="position: relative;">O(1)O(1)查询

我们只要把标准的ST表的递推公式改一下就可以了

st[i][j]=gcd(st[i][j−1],st[i+2j−1][j−1]" role="presentation" style="position: relative;">st[i][j]=gcd(st[i][j−1],st[i+2j−1][j−1]st[i][j]=gcd(st[i][j−1],st[i+2j−1][j−1]

最暴力的方法是直接枚举所有区间gcd值,再求区间个数,然后将x对应的区间个数保存在一个哈希表中,这种算法时间复杂度为O(n2)" role="presentation" style="position: relative;">O(n2)O(n2),实际上考虑到STL的map使用红黑树实现,查询为O(log2n)" role="presentation" style="position: relative;">O(log2n)O(log2n),时间复杂度还会更高

从枚举的过程中我们可以发现,固定区间左端点l,枚举右端点r时,很多区间的gcd值是和上一个区间相同的,导致这一次枚举是重复且无用的.因此,我们应该用更高效的方法去枚举区间.

因此,我们用二分查找去枚举gcd每次变化的位置,假设变化的位置为k(即gcd([l,k])!=gcd(l,k+1),则值为gcd([l,k])的区间个数会增加k-l+1个.这样重复枚举的情况会被消除.

由数论知识得左端点固定,随着右端点右移,gcd会变化log2n" role="presentation" style="position: relative;">log2nlog2n次!

(区间gcd的值必须是ai的因数,而且每次减少至少要除2(少了一个公因数,最小是2))

所以总时间复杂度为O(nlog2n)" role="presentation" style="position: relative;">O(nlog2n)O(nlog2n)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#define maxn 100005
#define maxlog 32
#define INF 10000005
using namespace std;
int a[maxn];
inline int gcd(int a,int b) {
return b==0?a:gcd(b,a%b);
}
struct sparse_table {//ST表
int st[maxn][maxlog];
void init(int *a,int n) {
for(int i=1; i<=n; i++) st[i][0]=a[i];
for(int j=1; (1<<j)<=n; j++) {
for(int i=1; i+(1<<j)-1<=n; i++) {
st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l,int r) {
if(l==r) return st[l][0];
int k=log2(r-l+1);
return gcd(st[l][k],st[r-(1<<k)+1][k]);
}
};
int n,q;
sparse_table gcda;
map<int,long long>ans;//预处理答案
int bin_search(int L,int R,int sta,int v){//二分查找区间gcd变化的位置的前一位
int l=L,r=R;
int ans=INF;
while(l<=r){
int mid=(l+r)>>1;
if(gcda.query(sta,mid)==v){
l=mid+1;
}else{
r=mid-1;
ans=min(ans,mid-1);
}
}
if(ans==INF) return r;
else return ans;
}
int main() {
// freopen("input.txt","r",stdin);
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
gcda.init(a,n);
ans.clear();
int now_gcd,now_pos;
for(int i=1; i<=n; i++) {
now_gcd=a[i];
now_pos=i;
while(1) {
int pre_pos=now_pos;//原来的位置
now_pos=bin_search(pre_pos,n,i,now_gcd);//变化的位置,下一个数gcd不同
now_gcd=gcda.query(i,now_pos);//原来的gcd
ans[now_gcd]+=now_pos-pre_pos+1;//更新答案
if(now_pos<n) {
now_pos++;//左端点移动到下一个数,开始枚举新区间
now_gcd=gcda.query(i,now_pos);//更新新的gcd
} else break; }
}
scanf("%d",&q);
int x;
while(q--) {
scanf("%d",&x);
printf("%I64d\n",ans[x]);
}
}

Codeforces 475D 题解(二分查找+ST表)的更多相关文章

  1. P7599-[APIO2021]雨林跳跃【二分,倍增,ST表】

    正题 题目链接:https://www.luogu.com.cn/problem/P7599 题目大意 \(n\)棵树,在某棵树上时可以选择向左右两边第一棵比它高的树跳,现在\(q\)次询问从\([A ...

  2. bzoj3277 串 (后缀数组+二分答案+ST表)

    常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割 由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串 ...

  3. 【CSP膜你赛】柠檬的密码(manacher 二分 单调性 st表)

    题目描述 Lemon觉得他需要一个复杂的密码来保证他的帐号的安全.他经过多日思考,决定使用一个长度为奇数的回文串来作为他的密码.  但是这个回文串太长了,Lemon记不住,于是Lemon决定把它记在本 ...

  4. Codeforces 873E Awards For Contestants ST表

    原文链接https://www.cnblogs.com/zhouzhendong/p/9255885.html 题目传送门 - CF873E 题意 现在要给 $n(n\leq 3000)$ 个学生颁奖 ...

  5. [BZOJ3277/BZOJ3473] 串 - 后缀数组,二分,双指针,ST表,均摊分析

    [BZOJ3277] 串 Description 现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Solution 首先将所有串连 ...

  6. Codeforces 803G Periodic RMQ Problem ST表+动态开节点线段树

    思路: (我也不知道这是不是正解) ST表预处理出来原数列的两点之间的min 再搞一个动态开节点线段树 节点记录ans 和标记 lazy=-1 当前节点的ans可用  lazy=0 没被覆盖过 els ...

  7. 2019.03.04 bzoj5308: [Zjoi2018]胖(二分答案+st表)

    传送门 想题5分钟调题两小时系列 其实还是我tcl 读完题之后自然会知道一个关键点能够更新的点是一段连续的区间,于是我们对于每个点能到的左右区间二分答案,用ststst表维护一下查询即可. 代码: # ...

  8. 2018.10.14 NOIP训练 直线(二分答案+st表+切比雪夫距离转化)

    传送门 二分答案好题. 这已经是当年普及组模拟时挖的坑了233. 这道题还是很不错的. 考虑把坐标系转个45度再操作. 为了不爆精度可以直接转切比雪夫距离. 然后就直接二分答案. 其中竖线就按二分的答 ...

  9. 说一说ST表 讲一讲水题

    ST表 一.算法介绍 如何快速求解RMQ问题呢?暴力复杂度O(n),线段树复杂度O(n)~O(logn),要是数据规模达到10^7或者更高呢?我们需要一种可以做到O(1)查询的算法,这时就可以用到ST ...

随机推荐

  1. canvas合并两张图片

    解析: 原理是一样的 画多张图需要一张一张画 也就是等图片onload成功后处理 这里代码写的比较随意 实际用的时候可以小粉转一下 也非常简单.我懒~~ 么么.. newImage(text) { / ...

  2. Pymongodb

    首先安装pymongo模块 pip install pymongo 利用Python程序完成增删改查 import pymongo import json from bson import Objec ...

  3. 调整数组顺序使奇数位于偶数前面(python)

    题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 方法1:冒泡  O( ...

  4. Python的命令行参数(argparse)

    参考:https://www.cnblogs.com/lindaxin/p/7975697.html 参考:https://www.cnblogs.com/dengtou/p/8413609.html ...

  5. python-*args、**kargs用法

    可变位置参数: *args:是一个元组,传入的参数会被放进元组里.可变关键字参数: **kwargs:是一个字典,传入的参数以键值对的形式存放到字典里. def test1(*args): print ...

  6. asp.net大文件上传解决方案

    以ASP.NET Core WebAPI 作后端 API ,用 Vue 构建前端页面,用 Axios 从前端访问后端 API ,包括文件的上传和下载. 准备文件上传的API #region 文件上传  ...

  7. input el-input 只能输入正整数验证

    字母e在js中属于数字,所以一般的正则匹配 \d 是拦不住字母e 的 正确写法为: onKeypress="return (/[\d]/.test(String.fromCharCode(e ...

  8. [CSP-S模拟测试]:毛三琛(随机化+二分答案)

    题目传送门(内部题69) 输入格式 第一行正整数$n,P,k$.第二行$n$个自然数$a_i$.$(0\leqslant a_i<P)$. 输出格式 仅一个数表示最重的背包的质量. 样例 样例输 ...

  9. 【ArchSummit干货分享】个推大数据金融风控算法实践

    作者:个推高级数据工程师 晓骏 众所周知,金融是数据化程度最高的行业之一,也是人工智能和大数据技术重要的应用领域.随着大数据收集.存储.分析和模型技术日益成熟,大数据技术逐渐应用到金融风控的各个环节. ...

  10. GitHub最著名的20个Python机器学习项目

    GitHub最著名的20个Python机器学习项目 我们分析了GitHub上的前20名Python机器学习项目,发现scikit-Learn,PyLearn2和NuPic是贡献最积极的项目.让我们一起 ...