1 条题解

  • 0
    @ 2025-8-24 22:50:31

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar xieziheng
    还不是因为我动了资本的蛋糕

    搬运于2025-08-24 22:50:31,当前版本为作者最后更新于2023-09-27 22:09:09,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    这道题细节真多。

    首先容易得到一个简单的 dp,设 dpi,jdp_{i,j} 表示前 ii 个数已经做完,此时 iijj 位置的最小值。但是发现这个 dp 有很多位置是没有意义的。所以可以优化为设 dpi,0/1dp_{i,0/1} 表示前 ii 个数已经做完,此时 ii1/m1/m 位置的最小值。转移即:找到此时不在前 mm 个数中的第一个比 ii 大的数 jj,转移到 dpj,0/1dp_{j,0/1} 上。

    但是这样是 O(n2)O(n^2) 的,瓶颈在于找到 jj。发现这个可以提前预处理。具体而言,首先断环成链,把数组倍长,然后从小到大扫所有的长度为 kk 的区间,每次加一个,删一个,然后找到比第一个比区间左或右端点大的值。这个玩意做法挺多的。可以用树状数组维护。树状数组倍增:将区间内的数对应的位置记为 00,区间外的数对应的位置记为 11。开始为全 11,加进来一个就改成 00,删一个改成 11。查询就先查个当前位置前缀和,然后倍增找到第一个前缀和比这个严格大的位置,就行了。

    code:code:

    //#pragma GCC optimize("O2,O3,Ofast,inline,unroll-loops")
    //#pragma GCC target("avx2,sse4")
    #include <bits/stdc++.h>
    #define il inline
    #define RET {puts("0");continue;}
    using namespace std;
    typedef long long ll;
    il int read(){
        int x=0,c=getchar();
        while(c<'0' || c>'9') c=getchar();
        while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
        return x;
    }
    il int ab(int x){return x>0?x:-x;}
    il void cmin(ll &x,ll y){x=x>y?y:x;}
    const ll inf=1e18;
    const int N=2e6+5;
    int T,n,m,a[N],p[N],to[N][2];ll ans,dp[N][2];
    il ll dis(int x,int y){return min(ab(x-y),n-ab(x-y));}
    int tree[N];
    il void add(int x,int v){while(x<=n) tree[x]+=v,x+=(x&-x);}
    il int get(int x){
        int ret=0;
        while(x) ret+=tree[x],x-=(x&-x);
        return ret;
    }
    il int jump(int v){
        int x=0;
        for(int i=19;i>=0;--i) if(x+(1<<i)<=n && tree[x+(1<<i)]<=v) x+=(1<<i),v-=tree[x];
        return x+1;
    }
    il int f(int x){
        if(x<=0) return x+n;
        if(x>n) return x-n;
        return x;
    }
    int x,y,z;
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);ans=inf,x=0;
            for(int i=1;i<=n;++i) dp[i][0]=dp[i][1]=inf,tree[i]=0;
            for(int i=1;i<=n;++i) a[i]=read(),p[a[i]]=i,a[i+n]=a[i],add(i,1);
            for(int i=1;i<n+m;++i){
                add(a[i],-1);
                if(i>m) add(a[i-m],1);
                if(i>=m) to[a[i-m+1]][0]=jump(get(a[i-m+1])),to[a[i]][1]=jump(get(a[i]));
                if(i==m) x=jump(0);
            }
            if(x>n) RET
            dp[x][0]=dis(1,p[x]),dp[x][1]=dis(m,p[x]);
            for(int i=1;i<=n;++i){
                x=to[i][0],y=p[i],z=p[x];
                if(x>n) cmin(ans,dp[i][0]);
                else cmin(dp[x][0],dp[i][0]+dis(y,z)),cmin(dp[x][1],dp[i][0]+dis(f(y+m-1),z));
                x=to[i][1],y=p[i],z=p[x];
                if(x>n) cmin(ans,dp[i][1]);
                else cmin(dp[x][0],dp[i][1]+dis(f(y-m+1),z)),cmin(dp[x][1],dp[i][1]+dis(y,z));
            }
            printf("%lld\n",ans);
        }
        return 0;
    }
    
    • 1

    信息

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