Codeforces Round #769 D

D. New Year Concert

显然我们外层应该套一个线性dp
f[i]表示 前i个我们必须改几个的min
我们考虑转移:我们需要找到当前b[i]和其前面某一个组成一个区间不合法的 我们考虑越往前看 gcd减小 而 我们r-l+1区间长度增加
证明我们只有一个交点:
这样我们的转移就只有一个式子 f[i]=f[j-1]+1 要是我们该点没有禁制 那从前面一个转移即可 f[i]=f[i-1]
所以我们考虑对每一个b[i]求一遍他前面有没有点不合法 然后转移即可
求合不合法 因为固定了区间长度 我们利用ST表二分找区间gcd即可

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
const int M = 998244353;
const int mod = 998244353;
#define int long long
#define endl '\n'
#define all(x) (x).begin(),(x).end()
#define YES cout<<"YES"<<endl;
#define NO cout<<"NO"<<endl;
#define _ 0
#define pi acos(-1)
#define INF 0x3f3f3f3f3f3f3f3f
#define fast ios::sync_with_stdio(false);cin.tie(nullptr);
int f[N][30],n,a[N],dp[N];
void init(){
    for(int len=0;len<30;len++){
        for(int i=1;i+(1<<len)-1<=n;i++){
            if(!len)f[i][len]=a[i];
            else f[i][len]=__gcd(f[i][len-1],f[i+(1<<(len-1))][len-1]);
        }   //i+1<<len-1的话是包括了i 不用加1
    }
}
int query(int l,int r){
    int len=r-l+1;
    int k=log(len)/log(2);
    return __gcd(f[l][k],f[r-(1<<k)+1][k]); //而r-1<<k是没有包含到r所以要+1
}
void solve() {
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    init();
    for(int i=1;i<=n;i++){
        if(i==1){
            if(a[i]==1)dp[i]=1;
            else dp[i]=0;
            continue;
        }
        int l=1,r=i,ans=0;
        while(l<=r){
            int mid=l+r>>1;
            if(query(mid,i)==i-mid+1){ans=mid;break;}
            if(query(mid,i)<i-mid+1)l=mid+1;
            else r=mid-1;
        }
        if(ans)dp[i]=dp[ans-1]+1;
        else dp[i]=dp[i-1];
    }
    for(int i=1;i<=n;i++)cout<<dp[i]<<' ';
}
signed main(){
    fast
    int T;T=1;
    while(T--) {
        solve();
    }
    return ~~(0^_^0);
}
posted @ 2022-09-23 20:15  ycllz  阅读(17)  评论(0)    收藏  举报