【arc073f】Many Moves(动态规划,线段树)

【arc073f】Many Moves(动态规划,线段树)

题面

atcoder
洛谷

题解

\(f[i][j]\)表示第一个棋子在\(i\),第二个棋子在\(j\)的最小移动代价。
发现在一次移动结束之后,总是有一个棋子会动到当前位置,因此状态改为当前是第\(i\)次操作,第\(i\)次操作没有动的那个棋子在\(j\)位置时的最小代价。
把第一维省掉,用线段树动态维护这个数组。
每次枚举移动哪一个棋子,直接线段树对应修改即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 200200
#define lson (now<<1)
#define rson (now<<1|1)
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,Q,A,B,a[MAX];ll f[MAX];
struct SegMentTree
{
    ll t[MAX<<2],tag[MAX<<2],a[MAX];
    void pushup(int now){t[now]=min(t[lson],t[rson]);}
    void Build(int now,int l,int r)
    {
        if(l==r){t[now]=f[l]+a[l];return;}
        int mid=(l+r)>>1;tag[now]=0;
        Build(lson,l,mid);Build(rson,mid+1,r);
        pushup(now);
    }
    void puttag(int now,ll w){t[now]+=w;tag[now]+=w;}
    void pushdown(int now)
    {
        if(!tag[now])return;
        puttag(lson,tag[now]);
        puttag(rson,tag[now]);
        tag[now]=0;
    }
    void Modify(int now,int l,int r,int p,ll w)
    {
        if(l==r){t[now]=min(t[now],w);return;}
        int mid=(l+r)>>1;pushdown(now);
        if(p<=mid)Modify(lson,l,mid,p,w);
        else Modify(rson,mid+1,r,p,w);
        pushup(now);
    }
    void Modify(int now,int l,int r,int L,int R,int w)
    {
        if(L<=l&&r<=R){puttag(now,w);return;}
        int mid=(l+r)>>1;pushdown(now);
        if(L<=mid)Modify(lson,l,mid,L,R,w);
        if(R>mid)Modify(rson,mid+1,r,L,R,w);
        pushup(now);
    }
    ll Query(int now,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R)return t[now];
        int mid=(l+r)>>1;ll ret=4e18;pushdown(now);
        if(L<=mid)ret=min(ret,Query(lson,l,mid,L,R));
        if(R>mid)ret=min(ret,Query(rson,mid+1,r,L,R));
        return ret;
    }
}T1,T2;
ll Solve(int A,int B)
{
    memset(f,63,sizeof(f));f[B]=abs(A-a[1]);
    for(int i=1;i<=n;++i)T1.a[i]=-i,T2.a[i]=i;
    T1.Build(1,1,n);T2.Build(1,1,n);
    ll inf=f[0];f[B]=inf;f[1]=abs(A-a[1]);
    for(int i=2;i<=Q;++i)
    {
        ll ret=inf;
        ret=min(ret,T1.Query(1,1,n,1,a[i])+a[i]);
        ret=min(ret,T2.Query(1,1,n,a[i],n)-a[i]);
        T1.Modify(1,1,n,1,n,abs(a[i]-a[i-1]));
        T2.Modify(1,1,n,1,n,abs(a[i]-a[i-1]));
        T1.Modify(1,1,n,a[i-1],ret-a[i-1]);
        T2.Modify(1,1,n,a[i-1],ret+a[i-1]);
        ret=min(ret,f[i-1]+abs(a[i]-a[i-1]));
        f[i]=ret;
    }
    return f[Q];
}
int main()
{
    n=read();Q=read();A=read();B=read();
    for(int i=1;i<=Q;++i)a[i]=read();
    printf("%lld\n",min(Solve(A,B),Solve(B,A)));
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/10472691.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值