【2020.11.19提高组模拟】倍数区间interval 题解
【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 题解的更多相关文章
- 【2020.11.28提高组模拟】T1染色(color)
[2020.11.28提高组模拟]T1染色(color) 题目 题目描述 给定 \(n\),你现在需要给整数 \(1\) 到 \(n\) 进行染色,使得对于所有的 \(1\leq i<j\leq ...
- 【2020.11.28提高组模拟】T2 序列(array)
序列(array) 题目描述 给定一个长为 \(m\) 的序列 \(a\). 有一个长为 \(m\) 的序列 \(b\),需满足 \(0\leq b_i \leq n\),\(\sum_{i=1}^ ...
- 【2020.11.30提高组模拟】剪辣椒(chilli)
剪辣椒(chilli) 题目描述 在花园里劳累了一上午之后,你决定用自己种的干辣椒奖励自己. 你有n个辣椒,这些辣椒用n-1条绳子连接在一起,任意两个辣椒通过用若干个绳子相连,即形成一棵树. 你决定分 ...
- 【2020.11.30提高组模拟】删边(delete)
删边(delete) 题目 题目描述 给你一棵n个结点的树,每个结点有一个权值,删除一条边的费用为该边连接的两个子树中结点权值最大值之和.现要删除树中的所有边,删除边的顺序可以任意设定,请计算出所有方 ...
- JZOJ 【2020.11.30提高组模拟】剪辣椒(chilli)
题目大意 给出一棵 \(n\) 个节点的树,删去其中两条边 使得分出的三个子树大小中最大与最小的差最小 分析 先一边 \(dfs\) 预处理出以 \(1\) 为根每个点的 \(size\) 然后按 \ ...
- JZOJ 6904. 【2020.11.28提高组模拟】T3 树上询问(query)
题目 你有一棵 \(n\) 节点的树 ,回答 \(m\) 个询问,每次询问给你两个整数 \(l,r\) ,问存在多少个整数 \(k\) 使得从 \(l\) 沿着 \(l \to r\) 的简单路径走 ...
- 11.5NOIP2018提高组模拟题
书信(letter) Description 有 n 个小朋友, 编号为 1 到 n, 他们每人写了一封信, 放到了一个信箱里, 接下来每个人从中抽取一封书信. 显然, 这样一共有 n!种拿到书信的情 ...
- 【2020.12.03提高组模拟】A组反思
估计:40+10+0+0=50 实际:40+10+0+0=50 rank40 T1 赛时看到\(n,m\leq9\),我当机立断决定打表,暴力打了几个点之后发现在\(n\ne m\)且\(k\ne0\ ...
- 【2020.12.01提高组模拟】卡特兰数(catalan)
题目 题目描述 今天,接触信息学不久的小\(A\)刚刚学习了卡特兰数. 卡特兰数的一个经典定义是,将\(n\)个数依次入栈,合法的出栈序列个数. 小\(A\)觉得这样的情况太平凡了.于是,他给出了\( ...
- 【2020.12.01提高组模拟】A组反思
105,rk45 T1 赛时一开始先打了\(m=0\)的情况,也就是普通的卡特兰数,然后打了暴力,样例过了,把样例改改就不行了,原因没有保证是枚举的是合法的出栈序列 得分:\(WA\&TLE1 ...
随机推荐
- 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
在 AI 编程领域国内外有一堆能叫的上号的应用: Cursor Windsurf Trae 阿里的「通义灵码」 百度的「文心快码」 字节跳动的「MarsCode」 科大讯飞的「iFlyCode」 Gi ...
- Selenium KPI接口 附件上传
实现功能 拖拽图片到百度上传图片搜索功能区域. 定位.send_keys(r'图片路径') 导入相关包 from selenium import webdriver from time import ...
- Caused by: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String 解决办法
使用MyBatis 更新数据库数据的时候 遇到了这个错误: Caused by: java.lang.IllegalArgumentException: invalid comparison: jav ...
- nodejs 图片添加水印(png, jpeg, jpg, gif)
同步发布:https://blog.jijian.link/2020-04-17/nodejs-watermark/ nodejs 作为一个脚本语言,图片处理这方面有点弱鸡,无法跟 php 这种本身集 ...
- [tldr]github仓库添加release
作为一个开源项目开发者,并且把自己的代码仓库托管到了github上面,所以,可以在github上提供自己的程序的release 这通常是通过二进制可执行文件的方式提供 新建草稿 点击create a ...
- [tldr] GO泛型编程
最少的内容简述如何在GO中使用泛型编程 函数泛型 func f[T any](s Set[T]) { } 在函数声明的时候添加一个[]作为泛型的说明, 在使用的时候是可以自动推断 很多时候, any的 ...
- go gin web服务器使用fvbock/endless优雅地重启或停止
gin使用fvbock/endless gin 正常使用注册路由时: package main import "github.com/gin-gonic/gin" func mai ...
- MySQL 8.0 误删了root用户怎么办
MySQL 8.0 误删了root用户怎么办 修改配置文件 修改配置文件,让其可以无账号登录 默认的文件为: /etc/my.cnf 添加:skip-grant-tables 重启服务 service ...
- Windows 10右键添加 "在此处打开命令窗口" 菜单
1.添加右键菜单的两种效果: 第一种是在 桌面/文件夹窗口中/选中文件夹上直接点击右键,显示"在此处打开命令窗口"选项,如图: 第二种是在 桌面/文件夹窗口中/选中文件夹上按住Sh ...
- Open diary(每天更新)
.col-md-8 img { display: none } .comment img { display: unset } 这是一个open diary,就是公开日记. 为什么标题用英文呢?因为觉 ...