区间
【题目简要】

\(\color{goldenrod}{数据范围}\)

【标签】
- 模拟,倍增
【思路】
-
我们要匹配出序列中第\(i\)个位置对应的最小位置\(j\),使满足\(i<j\)且\(a_i \perp a_j\),我们再将\(j\) 赋值到 \(b\) 数组。
-
接着我们将\(b\)数组求后缀最小值,得到数组\(bb\),便能发现一个很神奇的事:以第\(i\)个位置为首,最长的子区间的末尾为\(bb_i\)。
-
下面有点难表达,看下面的程序吧!φ(>ω<*)
【程序】
#include <bits/stdc++.h>
using namespace std;
int n,m,ans,tot,ma;
bool b[100005],kg;
int zs[10005],dy[100005],a[100005];
int c[10005],f[100005];
int jump[100005][20];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
f[i]=n+1;
ma=max(ma,a[i]);
}
for(int i=2; i<=sqrt(ma); i++)
if(!b[i]) for(int j=i+i; j<=ma; j+=i) b[j]=true;
for(int i=2; i<=ma; i++)
if(!b[i]) zs[++tot]=i,dy[i]=tot;
for(int i=1; i<=n; i++)
{
int d=a[i];
for(int j=1; j<=tot; j++)
{
if(d==1)break;
if(d%zs[j]==0)
{
f[c[j]]=min(f[c[j]],i);
c[j]=i;
while(d%zs[j]==0)
d/=zs[j];
}
}
}
for(int i=n-1; i>=1; i--)f[i]=min(f[i+1],f[i]);
for(int i=1; i<=n; i++)
jump[i][0]=f[i];
for(int i=1; i<=17; i++)
for(int j=1; j<=n; j++)
if(jump[j][i-1]!=n+1)jump[j][i]=jump[jump[j][i-1]][i-1];
else jump[j][i]=n+1;
for(int i=1; i<=m; i++)
{
int l,r;
ans=1;
scanf("%d%d",&l,&r);
for(int j=17; j>=0; j--)
if(jump[l][j]<=r)
{
ans=ans+(1<<j);
l=jump[l][j];
}
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号