区间

【题目简要】

\(\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;
}
posted @ 2021-08-19 08:31  zeromclai  阅读(86)  评论(0)    收藏  举报