1 条题解

  • 0
    @ 2025-8-24 23:12:04

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar UniGravity
    好菜阿。

    搬运于2025-08-24 23:12:04,当前版本为作者最后更新于2025-03-31 07:53:47,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    鲜花:Milthm 真的很好玩!111

    感谢 @良心WA题人 指出了题解复杂度分析的错误。重写了复杂度分析。

    首先有一种 O(n2logn)O(n^2\log n) 的做法:对于每个基准 xx 都二分出最大的 mm。发现 check 时每次贪心找最前面的某个值配对,这样单次 check 是 O(n)O(n) 的。

    考虑优化单次 check 的复杂度,一次快速跳一整个长度为 mm 的段。发现我们可以预处理某个数 xx 后第一个 x+1x+1 的位置,通过倍增即可找出某个数后最小合法的 x+m1x+m-1 的位置。同时再通过倍增找出 x+m1x+m-1 后下一个 xx 的位置。这样 check 就是 O(clogn)O(c\log n) 的(cc 是本次 check 合法的段数,因此实现时如果碰到不合法的就立即跳出)。

    然后观察 cc 和什么有关,发现 ccmin(mini=xx+m1cnti,k)\min(\min_{i=x}^{x+m-1}cnt_i,k),其中 cnticnt_i 代表值 ii 的出现次数。

    考虑双指针加上 check 判断。假设当前的区间为 [x,x+m1][x,x+m-1],则发现每次 check 后左右端点至少会有一个向右移动:

    • 如果 check 失败,则已经找到了 xx 的答案,接下来 xx+1,mm1x\gets x+1,m\gets m-1。发现左端点右移,右端点不变。
    • 如果 check 成功,则变成 mm+1m\gets m+1,右端点向右。

    若每次 check 复杂度只和上次区间移动时变化的端点的出现次数(即 cntlcnt_lcntrcnt_r)有关(这样显然比 min(mini=xx+m1cnti,k)\min(\min_{i=x}^{x+m-1}cnt_i,k) 的限制松),因为 i=1ncnti=n\sum_{i=1}^ncnt_i=n,且 cnticnt_i 最多只会在 l,rl,r 移动到的时候扫两遍,因此所有的 check 的 cc 之和的上界为 O(n)O(n)

    于是做完了。时间复杂度 O(nlogn)O(n\log n)

    const int N=2000005;
    int n,k,V,a[N],nxt[22][N],nv[22][N],pre[N];
    
    il bool check(int bs,int m){
        int p=pre[bs],c,p1,p2;if(!p)return 0;
        // cerr<<"check "<<bs<<' '<<m<<'\n';
        forto(i,1,k){
            c=m-1,p1=p;
            forbk(t,21,0)if((1<<t)<=c)c-=1<<t,p1=nxt[t][p1];//,cerr<<t<<','<<p1<<' ';cerr<<'\n';
            // cerr<<p<<' '<<m<<' '<<p1<<'\n';
            if(!p1)return 0;
            if(i==k)return 1;
            p2=p;
            forbk(t,21,0)if(nv[t][p2]&&nv[t][p2]<=p1)p2=nv[t][p2];
            p2=nv[0][p2];if(!p2)return 0;
            p=p2;
        }
        return 1;
    }
    
    signed main(){
        n=read(),k=read(),V=read();
        forto(i,1,n)a[i]=read();
        forbk(i,n,1)nv[0][i]=pre[a[i]],nxt[0][i]=pre[a[i]+1],pre[a[i]]=i;
        // forto(i,1,n)cerr<<nxt[0][i]<<' ';cerr<<'\n';
        forto(t,1,21)forto(i,1,n)nxt[t][i]=nxt[t-1][nxt[t-1][i]],nv[t][i]=nv[t-1][nv[t-1][i]];
        int ans=0;
        forto(i,1,V){
            while(check(i,ans+1))ans++;
            printf("%d ",ans);
            if(ans)ans--;
        }
        puts("");
        return 0;
    }
    
    • 1

    信息

    ID
    10974
    时间
    2000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者