【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. 【CIM信息整合】关于三维建筑模型

    还是无暇细细检索并总结列出有逻辑的明确表述,以下很多地方都是人云亦云的复制,自己也没太搞清 1.5 三维建筑模型 CIM中三维建筑模型主要表达建(构)筑物的空间位置.几何形态及外观效果等. 在建筑相关 ...

  2. Qt修改exe文件图标

    修改Qt生成exe的图标以及软件标题图标 目录 修改Qt生成exe的图标以及软件标题图标 简介 QtCreator下添加exe图标 直接添加.ico 通过.rc文件修改 Visual Studio下添 ...

  3. NumPy学习5

    今天学习了11, NumPy数组元素增删改查NumPy 数组元素的增删改查操作,主要有以下方法:数组元素操作方法函数名称 描述说明resize 返回指定形状的新数组.append 将元素值添加到数组的 ...

  4. 使用Win32控制台实现socket通信

    本示例使用Win32控制台实现socket通信 ,可多个客户端同时连接服务器. 服务端代码: #include <iostream> #include <winsock2.h> ...

  5. centos安装JDK11

    (一)建立目录 mkdir /home/jdk11 (二) 上传JDK的离线liunx包 cd /home/jdk11 示例:jdk-11.0.19_linux-x64_bin.tar.gz (三) ...

  6. WebKit Inside: px 与 pt

    前端CSS中的px是物理像素,还是逻辑像素? 它和iOS中的pt是怎样的关系? 下面我们就来看下CSS中的px实现. 假设有如下CSS字号设置: div { font-size: 100px; } 最 ...

  7. 大型通用电子制造执行系统(MES)

    ​ 简介: 系统参考西门子MOM智能制造Opcenter工业软件制造执行系统Camstar电子套件人机料法环数据建模业务对象和车间生产执行事务逻辑,采用面向对象分层设计与C#编程开发:包含电子制造企业 ...

  8. C# Office COM 加载项

    Office COM 加载项开发笔记 一.实现接口 IDTExtensibility2 这是实现 Office COM 加载项最基本的接口 添加 COM 引用 Microsoft Add-In Des ...

  9. 使用Python解决三体问题

    引言 在物理学中,三体问题是一个经典的动态系统问题,它描述了三个天体之间的相互引力作用和运动规律.三体问题最著名的挑战在于它无法通过简单的解析公式来解决,换句话说,三体问题是一个不可解析的问题.尽管如 ...

  10. 结合钉钉机器人用python写监控打印机碳粉状态程序

    点击查看代码 from pysnmp.hlapi import * import requests import json # 配置信息 PRINTER_IP = '1.1.1.1' # 打印机IP ...