【2020.11.19提高组模拟】倍数区间interval 题解

题目描述

定义在序列\(a_1,a_2,\dots,a_n\)上的合法区间\([L,R]\)为满足\(\exists k\in [L,R],\forall i\in [L,R],a_k\mid a_i\)。

即存在一个数\(a_k\),满足区间内所有的数都是\(a_k\)的倍数。

求最长的合法区间长度并输出这些区间的左端点。

Solution

又是区间问题。

首先找到几个结论:

1.\(a_k\)一定是这个区间内最小的数。

2.\(a_k\mid 区间gcd\)。

第一个很显然,倍数当然要大于或等于自己的。

第二个也很显然,\(区间gcd\)就是这个区间内所有数的因数的交集。

那么我们可以用ST表预处理出区间\(gcd\),然后对于每个数当作它是那个\(a_k\),左右各自二分出最远的合法区间即可。

复杂度\(O(n\log^2 n)\)。

Code-ST

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define debug printf("Now is %d\n",__LINE__);
using namespace std; template<class T>inline void read(T&x)
{
char ch=getchar();
int fu;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
x*=fu;
}
inline int read()
{
int x=0,fu=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
int g=0;
if(x<0) x=-x,putchar('-');
do{G[++g]=x%10;x/=10;}while(x);
for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar(' ');
}
int n;
int lg[1000010];
int gcd[1000010][24];
int GCD(int a,int b)
{
// cout<<"gcd a="<<a<<" b="<<b<<" ";
int t;
while(b)
{
t=b;
b=a%b;
a=t;
}
// cout<<"gcd="<<a<<endl;
return a;
}
void pre()
{
lg[1]=0;
for(re int i=2;i<1000010;i++) lg[i]=lg[i>>1]+1;
for(re int j=1;j<=22;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
gcd[i][j]=GCD(gcd[i][j-1],gcd[i+(1<<(j-1))][j-1]);
}
}
}
int ask(int l,int r)
{
return GCD(gcd[l][lg[r-l+1]],gcd[r-(1<<lg[r-l+1])+1][lg[r-l+1]]);
}
int maxans;
int ans[500010];
int sze;
//out:sze-maxans\nans
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) gcd[i][0]=read();
pre();
for(int k=1,L,R,l,r,mid;k<=n;k++)
{
l=1,r=k;
while(l!=r)
{
mid=(l+r)/2;
if(ask(mid,k)%gcd[k][0]==0) r=mid;
else l=mid+1;
}
L=l;
l=k,r=n;
while(l<r)
{
mid=(l+r+1)/2;
if(ask(k,mid)%gcd[k][0]==0) l=mid;
else r=mid-1;
}
R=r;
// cout<<L<<" "<<R<<endl;
if(R-L>maxans) maxans=R-L,sze=0;
if(R-L==maxans&&ans[sze]!=L) ans[++sze]=L;
}
printf("%d %d\n",sze,maxans);
for(int i=1;i<=sze;i++) write(ans[i]);
return 0;
}

upd

但是其实有一种\(O(n\log n)\)的做法!

我在考试的时候打暴力时打的就是这样的优化,我觉得它的时间过不去,但是其实比上面的算法还要暴力,复杂度还要小!

利用第一个性质——自己的倍数的倍数也是自己的倍数。

那么每次对一个数扩展完之后记录一下区间的左右端点。

之后对后面的数\(a_k\)扩展时,若发现有一个\(a_k\mid a_i(i<k)\),那么当前的区间的左端点可以直接跳转到\(a_i\)的合法区间左端点。同理,右端点也可以跳。

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define debug printf("Now is %d\n",__LINE__);
using namespace std; template<class T>inline void read(T&x)
{
char ch=getchar();
int fu;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
x*=fu;
}
inline int read()
{
int x=0,fu=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
int g=0;
if(x<0) x=-x,putchar('-');
do{G[++g]=x%10;x/=10;}while(x);
for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar(' ');
}
int n;
int a[500010];
int kmp[500010],gkd[500010];
int ans;
int L[500010];
int sze;
//out:size-ans\nL
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
n=read();
for(re int i=1;i<=n;i++) a[i]=read(),kmp[i]=i;
for(re int i=1,l,r;i<=n;i++)
{
// debug cout<<"i="<<i<<endl;
for(l=i-1,r=i+1;l>0;l--)
{
// debug
if(a[l]<a[i]||a[l]%a[i]) break;
// debug
// cout<<"l="<<l<<endl;
l=kmp[l];
r=max(r,gkd[l]);
}
l++;
kmp[i]=l;
for(;r<=n;r++)
{
// debug
if(a[r]<a[i]||a[r]%a[i]) break;
// debug
}
r--;
gkd[i]=r;
if(r-l>ans)
{
ans=r-l;
sze=0;
}
if(r-l==ans&&L[sze]!=l)
{
L[++sze]=l;
}
}
printf("%d %d\n",sze,ans);
for(int i=1;i<=sze;i++)
{
write(L[i]);
}
return 0;
}

小结

注意最后的结果要判重哦!否则\(score-=30pts\)!

【2020.11.19提高组模拟】倍数区间interval 题解的更多相关文章

  1. 【2020.11.28提高组模拟】T1染色(color)

    [2020.11.28提高组模拟]T1染色(color) 题目 题目描述 给定 \(n\),你现在需要给整数 \(1\) 到 \(n\) 进行染色,使得对于所有的 \(1\leq i<j\leq ...

  2. 【2020.11.28提高组模拟】T2 序列(array)

    序列(array) 题目描述 ​给定一个长为 \(m\) 的序列 \(a\). 有一个长为 \(m\) 的序列 \(b\),需满足 \(0\leq b_i \leq n\),\(\sum_{i=1}^ ...

  3. 【2020.11.30提高组模拟】剪辣椒(chilli)

    剪辣椒(chilli) 题目描述 在花园里劳累了一上午之后,你决定用自己种的干辣椒奖励自己. 你有n个辣椒,这些辣椒用n-1条绳子连接在一起,任意两个辣椒通过用若干个绳子相连,即形成一棵树. 你决定分 ...

  4. 【2020.11.30提高组模拟】删边(delete)

    删边(delete) 题目 题目描述 给你一棵n个结点的树,每个结点有一个权值,删除一条边的费用为该边连接的两个子树中结点权值最大值之和.现要删除树中的所有边,删除边的顺序可以任意设定,请计算出所有方 ...

  5. JZOJ 【2020.11.30提高组模拟】剪辣椒(chilli)

    题目大意 给出一棵 \(n\) 个节点的树,删去其中两条边 使得分出的三个子树大小中最大与最小的差最小 分析 先一边 \(dfs\) 预处理出以 \(1\) 为根每个点的 \(size\) 然后按 \ ...

  6. JZOJ 6904. 【2020.11.28提高组模拟】T3 树上询问(query)

    题目 你有一棵 \(n\) 节点的树 ,回答 \(m\) 个询问,每次询问给你两个整数 \(l,r\) ,问存在多少个整数 \(k\) 使得从 \(l\) 沿着 \(l \to r\) 的简单路径走 ...

  7. 11.5NOIP2018提高组模拟题

    书信(letter) Description 有 n 个小朋友, 编号为 1 到 n, 他们每人写了一封信, 放到了一个信箱里, 接下来每个人从中抽取一封书信. 显然, 这样一共有 n!种拿到书信的情 ...

  8. 【2020.12.03提高组模拟】A组反思

    估计:40+10+0+0=50 实际:40+10+0+0=50 rank40 T1 赛时看到\(n,m\leq9\),我当机立断决定打表,暴力打了几个点之后发现在\(n\ne m\)且\(k\ne0\ ...

  9. 【2020.12.01提高组模拟】卡特兰数(catalan)

    题目 题目描述 今天,接触信息学不久的小\(A\)刚刚学习了卡特兰数. 卡特兰数的一个经典定义是,将\(n\)个数依次入栈,合法的出栈序列个数. 小\(A\)觉得这样的情况太平凡了.于是,他给出了\( ...

  10. 【2020.12.01提高组模拟】A组反思

    105,rk45 T1 赛时一开始先打了\(m=0\)的情况,也就是普通的卡特兰数,然后打了暴力,样例过了,把样例改改就不行了,原因没有保证是枚举的是合法的出栈序列 得分:\(WA\&TLE1 ...

随机推荐

  1. 初识 PHP 7 源码整体框架

    目录 PHP 7 语言的执行原理 编译型语言与解释型语言 PHP 7 语言的执行原理 我们常用的高级语言有很多种,比较出名的有C\C++.Python.PHP.Go.Pascal等.而这些语言根据运行 ...

  2. Windows 提权-服务_弱服务权限

    本文通过 Google 翻译 Weak Service Permissions – Windows Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行 ...

  3. Joker 可视化开发平台全局方法使用指南

    在 Joker 可视化开发平台中,全局方法是实现公共业务逻辑的有力工具,它能跨越组件和页面文件的界限,让开发者快速调用,显著提升开发效率.下面将详细介绍全局方法在平台中的使用方式. 一.全局方法的定义 ...

  4. 万字长文详解SIFT特征提取

    本文对 SIFT 算法进行了详细梳理.SIFT即尺度不变特征变换(Scale-Invariant Feature Transform),是一种用于检测和描述图像局部特征的算法.该算法对图像的尺度和旋转 ...

  5. Linux如何从命令行卡死的进程中退出?

    Linux如何从命令行卡死的进程中退出? 不知道大家在使用Linux的时候,会不会遇到一些命令,有可能卡顿,有可能执行时间过长(比如使用 find 查找某个文件),这个时候我不想继续执行这个命令了,说 ...

  6. C# 13 中的新增功能实操

    前言 今天大姚带领大家一起来看看 C# 13 中的新增几大功能,并了解其功能特性和实际应用场景. 前提准备 要体验 C# 13 新增的功能可以使用最新的 Visual Studio 2022 版本或 ...

  7. 【Linux】5.5 Shell运算符

    Shell运算符 Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符 关系运算符 布尔运算符 字符串运算符 文件测试运算符 原生bash不支持简单的数学运算,但是可以通过其他命令来实现 ...

  8. 新建一个空的 ASP.NET Core Web Application

    前言 Visual Studio 2017 下操作 1. 新建项目 2. 新建空的 ASP.NET Core Web Application 确定后,需要一小点的时间等待依赖库载入... 3. 新建完 ...

  9. 解决Linux中网络重启后ip变化的问题

    一.在Linux中输入命令, 编辑 ens33 网卡的网络配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 二.在配置中作如下修改 将 BOOTP ...

  10. Java 中的 young GC、old GC、full GC 和 mixed GC 的区别是什么?

    Java 中的 young GC.old GC.full GC 和 mixed GC 的区别 在 Java 中,垃圾回收(GC)可以分为几种不同类型,包括 young GC.old GC.full G ...