Loading

AtCoder-abc265_f Manhattan Cafe

Manhattan Cafe

dp 前缀和优化

很容易想到 \(dp\) 的状态

\(dp[i][j][k]\) 表示前 \(i\) 个点,\(r_x\)\(p_x\) 的差值和为 \(j\)\(r_x\)\(q_x\) 的差值和为 \(k\)

这样的话直接枚举第 \(i\) 组点中,我们取的点的位置,能够做到 \(O(D)\) 的复杂度转移

总的时间复杂度为 \(O(nD^3)\),显然超时

考虑转移的时间进行优化,我们会发现选取的位置不同,会有不同的规律,假设 \(p_i = 1\)\(q_i = 4\)

如果选取的点 \(r_i\)\([1, 4]\),那么 \(|p_i-x_i|+|q_i-x_i| = |p_i - q_i|\),也就是 \(dp[i][j][k] = \sum dp[i-1][j-x][k-y],\) \(x+y=|p_i - q_i|\),此时一定是从一个 \(j'+k'\) 为定值的之前的状态转移过来,这个定值就是 \(j+k-|p_i-q_i|\)

如果选取的点在 \([1, 4]\) 之外,那么与上一个转移相反,这种是从满足 \(j'-k'\) 为定值的状态转移过来,这个定值就是 \(|p_i-q_i|\)

上述转移文字可能不清楚,可以自己列表格,对每个离散点判断一下他应该从什么状态转移过来,就可以理解

因此考虑做两个对角线前缀和,每次 \(O(1)\) 转移

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1e3 + 10;
typedef long long ll;
const ll mod = 998244353;
ll dp[maxn][maxn], dig[maxn][maxn], a_dig[maxn][maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, d;
    cin >> n >> d;
    vector<int>q(n), p(n);
    for(int i=0; i<n; i++) cin >> q[i];
    for(int i=0; i<n; i++) cin >> p[i]; 
    dp[0][0] = 1;
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<=d; j++)
        {
            for(int k=0; k<=d; k++)
            {
                dig[j][k] = a_dig[j][k] = dp[j][k];
                if(j && k < d)
                    dig[j][k] += dig[j - 1][k + 1];
                if(j && k)
                    a_dig[j][k] += a_dig[j - 1][k - 1];
            }
        }
        for(int j=0; j<=d; j++)
        {
            for(int k=0; k<=d; k++)
            {
                int s = abs(p[i] - q[i]);
                dp[j][k] = 0;
                if(k + j >= s)
                {
                    int l = max(0, j - s), r = k + j - s - max(0, k - s);
                    if(l == 0) dp[j][k] += dig[r][k - s + j - r];
                    else dp[j][k] += dig[r][k - s + j - r] - dig[l - 1][k - s - l + j + 1];
                }
                if(k > s && j)
                    dp[j][k] += a_dig[j - 1][k - 1 - s];
                if(j > s && k)
                    dp[j][k] += a_dig[j - 1 - s][k - 1];
                dp[j][k] %= mod;
            }
        }
    }
    ll ans = 0;
    for(int i=0; i<=d; i++)
        for(int j=0; j<=d; j++)
            ans += dp[i][j];
    cout << ans % mod << endl;
    return 0;
}
posted @ 2022-08-23 23:45  dgsvygd  阅读(114)  评论(2编辑  收藏  举报