场上过不去的简单题

\(\tt\bf{I. AT\_abc370\_f}\)

首先套路的破换成链,然后二分答案 \(p\)

对于每一个二分的答案 \(p\),考虑先二分出 \(f_{i,0}\) 表示 \(i\) 之后第一个满足 \(\sum\limits_{j=i}^{f_{i,0}} a_j\ge p\)\(f_{i,0}\),然后倍增的设 \(f_{i,j}\) 表示 \(i\) 之后满足 \(2^j\) 次上述条件的下标为 \(f_{i,j}\)。特殊的若不可以满足则令 \(f_{i,j}=-1\)

然后考虑枚举起点,直接暴力倍增计算出 \(k\) 次操作之后的答案即可。时间复杂度为 \(O(n\log^2n)\)。代码有点混乱。

const int N=400100,mod=998244353;
int a[N],s[N],n,k,now,nxt[N][20],res;
bool chk(int p){
    memset(nxt,-1,sizeof nxt);
    F(i,1,n+n){
        int l=i,r=i+n-1,best=-2;
        r=min(r,n+n);
        while(l<=r){
            int mid=l+r>>1;
            if(s[mid]-s[i-1]>=p)best=mid,r=mid-1;
            else l=mid+1;
        }
        nxt[i][0]=best+1;
    }
    nxt[n+n+1][0]=n+n+1;
    F(i,1,19)
        F(j,1,n+n+1)
            if(~nxt[j][i-1])
                nxt[j][i]=nxt[nxt[j][i-1]][i-1];
    // F(i,1,n+n)
    //     cout<<i<<": "<<nxt[i][0]<<' '<<nxt[i][1]<<" qwq\n";
    int cnt=0;
    F(i,1,n){
        int x=i;
        G(j,19,0)
            if(k>>j&1){
                x=nxt[x][j];
                if(x==-1||x>i+n)break;
            }
        if(x!=-1&&x<=i+n)
            ++cnt;
    }
    if(cnt)
        res=n-cnt;
    return !!cnt;
}
signed main(){
    cin>>n>>k;
    F(i,1,n)cin>>a[i],s[i]=s[i-1]+a[i];
    F(i,1,n)a[i+n]=a[i],s[i+n]=s[i+n-1]+a[i];
    int l=1,r=s[n],best=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(chk(mid))best=mid,l=mid+1;
        else r=mid-1;
    }
    cout<<best<<' '<<res<<'\n';
}

posted @ 2024-09-09 15:07  yhbqwq  阅读(95)  评论(0编辑  收藏  举报