CF704B Ant Man 题解

集训队作业居然有 *2500 的题。/jy/jy/jy

把贡献拆一下,f(i,j)f(i,j)i,ji,j 的贡献是独立的,很方便我们分别考虑计算。

具体来说贡献有四类:

  • 左边比 ii 大:xi+bi-x_i+b_i
  • 左边比 ii 小:xi+cix_i+c_i
  • 右边比 ii 大:xi+di-x_i+d_i
  • 右边比 ii 小: xi+aix_i+a_i

然后按照 JOI 摩天大楼 那个题的套路来做,dpi,jdp_{i,j} 表示填了前 ii 个数,当前有 jj 个连续段的最小值。

从小到大加入数字,转移分三类讨论:

  • 当前点是 ss,分成新开一段(右边比他大)和接在一段边上(右边比他小)两种;
  • 当前点是 ee,分成新开一段(左边比他大)和接在一段边上(左边比他小)两种;
  • 当前点是普通点,分成新开一段(左右都比他大),合并两段(左右都比他小),接在一段左边(左边比他大,右边比他小),接在一段右边(左边比他小,右边比他大)四种。

这里有一个坑点是有些状态是不合法的,具体来说有下面五种,要特判掉:

  • 数组越界了的;
  • 合并成 00 段的;
  • s,es,e 都已经填了,当前点不是 nn 却已经缩成了一段的;
  • 只填了 ss,只有一段却接在了这段左边的;
  • 只填了 ee,只有一段却接在了这段右边的。

时间复杂度 O(n2)O(n^2)

#include<iostream>
#include<cstdio>
using namespace std;
#define int long long
int n,s,t,cnt,x[5001],a[5001],b[5001],c[5001],d[5001],dp[5001][5001];
signed main()
{
    cin>>n>>s>>t;
    for(int i=1;i<=n;++i)
        cin>>x[i];
    for(int i=1;i<=n;++i)
        cin>>a[i];
    for(int i=1;i<=n;++i)
        cin>>b[i];
    for(int i=1;i<=n;++i)
        cin>>c[i];
    for(int i=1;i<=n;++i)
        cin>>d[i];
    for(int i=0;i<=n;++i)
        for(int j=0;j<=n;++j)
            dp[i][j]=1e18;
    dp[0][0]=0;
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<=n;++j)
        {
            if(j==1&&cnt==2)
                dp[i][j]=1e18;
            if(dp[i][j]<1e18)
            {
                if(s==i+1)
                {
                    if(j<n)
                        dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]-x[i+1]+d[i+1]);
                    if(j)
                        dp[i+1][j]=min(dp[i+1][j],dp[i][j]+x[i+1]+c[i+1]);
                }
                else if(t==i+1)
                {
                    if(j<n)
                        dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]-x[i+1]+b[i+1]);
                    if(j)
                        dp[i+1][j]=min(dp[i+1][j],dp[i][j]+x[i+1]+a[i+1]);
                }
                else
                {
                    if(j<n)
                        dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]-x[i+1]+b[i+1]-x[i+1]+d[i+1]);
                    if(j>1)
                        dp[i+1][j-1]=min(dp[i+1][j-1],dp[i][j]+x[i+1]+a[i+1]+x[i+1]+c[i+1]);
                    if(j)
                    {
                        if(t>i+1||j>1)
                            dp[i+1][j]=min(dp[i+1][j],dp[i][j]+d[i+1]+a[i+1]);
                        if(s>i+1||j>1)
                            dp[i+1][j]=min(dp[i+1][j],dp[i][j]+c[i+1]+b[i+1]);
                    }
                }
            }
        }
        cnt+=s==i+1||t==i+1;
    }
    cout<<dp[n][1]<<'\n';
    return 0;
}

怄火。挥手。转圈。街舞。跳跳。献吻。跳绳。激动。发抖。磕头。爱情。飞吻。左太极。右太极。回头。