Loading

题解:AT_arc073_d [ARC073F] Many Moves

题目链接:link

题意已经挺简易了,直接上思路吧。

我们设 \(f_{i,j}\) 表示当前在第 \(i\) 个时刻,一个棋子在 \(x_i\) 位置,另一个棋子在 \(j\) 位置的最小代价之和。

状态转移有两种:

\[f_{i,j}+|x_i-x-i+1|\rightarrow f_{i+1,j} \]

\[f_{i,j}+|j-x_i+1|\rightarrow f_{i+1,x_i+1} \]

我们可以使用线段树来维护 \(f_{i,\cdots}\),考虑每次从 \(f_{i,\cdots}\rightarrow f_{i+1,\cdots}\) 可以发现只需要做一个全局加和单点修改,同时还要维护 \(f_{i,j}+|j-x_i+1|\) 的最小值,这个拆开绝对值之后只需要算两段区间的 \(f_{i,j}+j,f_{i,j}-j\) 的最小值,这些都可以用线段树维护,这个算法的复杂度就是 \(\mathcal{O(n+Q\log n)}\)

上代码:

#include<bits/stdc++.h>
#define I using
#define AK namespace
#define IOI std
#define i_ak return
#define ioi  0
#define i_will signed
#define ak main
#define IMO ()
#define int long long
#define p1 (p<<1)
#define p2 ((p<<1)|1)
I AK IOI;
int n,q,A,B,a[200010],d[2][800010],b[800010];
void cl(int p){
    if(b[p]){
        b[p1]+=b[p];
        b[p2]+=b[p];
        d[0][p1]+=b[p];
        d[1][p1]+=b[p];
        d[0][p2]+=b[p];
        d[1][p2]+=b[p];
        b[p]=0;
    }
}
void build(int s,int t,int p){
    if(s==t){
        d[0][p]=min(abs(a[1]-A)+abs(s-B),abs(a[1]-B)+abs(s-A))-s;
        d[1][p]=min(abs(a[1]-A)+abs(s-B),abs(a[1]-B)+abs(s-A))+s;
        return;
    }
    int mid=s+t>>1;
    build(s,mid,p1);
    build(mid+1,t,p2);
    d[0][p]=min(d[0][p1],d[0][p2]);
    d[1][p]=min(d[1][p1],d[1][p2]);
}
void add(int x,int s,int t,int p,int c){
    if(s==t){
        d[0][p]=c-s;
        d[1][p]=c+s;
        return;
    }
    cl(p);
    int mid=s+t>>1;
    if(x<=mid)add(x,s,mid,p1,c);
    if(mid+1<=x)add(x,mid+1,t,p2,c);
    d[0][p]=min(d[0][p1],d[0][p2]);
    d[1][p]=min(d[1][p1],d[1][p2]);
}
void update(int l,int r,int s,int t,int p,int c){
    if(l>r)return;
    if(l<=s&&t<=r){
        d[0][p]+=c;
        d[1][p]+=c;
        b[p]+=c;
        return;
    }
    cl(p);
    int mid=s+t>>1;
    if(mid>=l)update(l,r,s,mid,p1,c);
    if(mid+1<=r)update(l,r,mid+1,t,p2,c);
    d[0][p]=min(d[0][p1],d[0][p2]);
    d[1][p]=min(d[1][p1],d[1][p2]);
}
int getsum(int l,int r,int s,int t,int p,int fg){
    if(l<=s&&t<=r)return d[fg][p];
    cl(p);
    int mid=s+t>>1,as=1e18;
    if(l<=mid)as=getsum(l,r,s,mid,p1,fg);
    if(mid+1<=r)as=min(as,getsum(l,r,mid+1,t,p2,fg));
    return as;
}
int query(int s,int t,int p){
    if(s==t)return d[0][p]+s;
    cl(p);
    int mid=s+t>>1;
    return min(query(s,mid,p1),query(mid+1,t,p2));
}
i_will ak IMO{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>q>>A>>B;
    for(int i=1;i<=q;i++)cin>>a[i];
    build(1,n,1);
    for(int i=2;i<=q;i++){
        int x=min(getsum(1,a[i],1,n,1,0)+a[i],getsum(a[i]+1,n,1,n,1,1)-a[i]);
        add(a[i-1],1,n,1,x);
        update(1,a[i-1]-1,1,n,1,abs(a[i]-a[i-1]));
        update(a[i-1]+1,n,1,n,1,abs(a[i]-a[i-1]));
    }
    cout<<query(1,n,1);
	i_ak ioi;
}

亲测可过,请勿抄袭!

posted @ 2025-06-09 15:42  盼满天繁星  阅读(16)  评论(0)    收藏  举报