题目
长度从1到n的每个区间都需要求最小的操作数。
题解思路
首先区间gcd(l,r)
定住一边的话是具有单调性的。
定住l增大r,因为出来的gcd肯定是不增的,也就是降序。
其次,利用st表,可以O1得出某段区间的gcd。之前的st表题目
为什么可以用呢?
因为只要我们的gcd能正确包括整个范围,不论有些地方重复覆盖了,也一样可以得出正确的gcd。这和最最大值是一个道理。
因为答案固定了1到n的区间,所以我们定住右端点r。往有可能的地方进行二分搜索出有可能等于区间大小的部分。
如果等于区间大小那么我们可以贪心的把r改成一个非常大的素数。这样gcd出来就只能是1了。
就相当于人造一堵墙。把有可能的gcd全堵成1了。
为什么贪心成改r呢?
r更有潜力阻止更多的gcd等于区间大小,改r即能挡住以l往前的点组成badgcd。还可以阻止l到r的点。
AC代码
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200100;
int n ;
int a[N] ;
int st[N][50] ;
void make_st()
{
int k = __lg(n) ;
for (int i = 1 ; i <= n ; i++ )
st[i][0] = a[i] ;
for (int j = 1 ; j <= k ; j++ )
for (int i = 1 ; i + (1<<j) -1 <= n ; i++ )
st[i][j] = __gcd(st[i][j-1],st[i+(1<<(j-1))][j-1] ) ;
}
int query(int l , int r )
{
int k = __lg(r-l+1) ;
return __gcd(st[l][k],st[r-(1<<k)+1][k] ) ;
}
void solve()
{
cin >> n ;
for (int i = 1 ; i <= n ; i++ )
cin >> a[i] ;
make_st() ;
int res = 0 ;
int q = -1 ;
int l = 1 ;
for (int r = 1 ; r <= n ; r++ )
{
int t1 = l , t2 = r ;
while ( t1 < t2 )
{
int mid = t1 + t2 >> 1 ;
if ( query(mid,r) >= r - mid + 1 )
t2 = mid ;
else
t1 = mid + 1 ;
}
l = t2 ;
if ( query(l,r) == r - l + 1 )
{
if ( l > q )
{
q = r ;
res ++ ;
}
}
cout << res << " " ;
}
cout << "\n" ;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
solve() ;
return 0 ;
}