https://codeforces.com/contest/1862/problem/F

补一篇题解。

首先dp[i][j]:表示前i个数,以a[j]结尾的所有方案数量。

那么,我们首先按照...j < i...j=i...j > i三个分类讨论,然后再按照上一维的状态dp[i - 1][x]转移。

1. j < i 

那么x的范围应该满足x <= j && a[x] <= a[j]。如何理解呢?会发现,只有这种情况,dp[i - 1][x]的数量才是全的。比如,如果x > j && x < i,a[x] <= a[j],此时的dp[i - 1][x]最后一部分已经是a[x]了,那么如果我们想要转移到dp[i][j],就肯定要把从j开始的一段全部变为a[j],但是会将a[i - 1] -> a[j],可以发现,此时我们假设的x其实已经变成了另外一个数了,那么状态都不对,则对应的方案数肯定也不对。差不多不合法的方案原因类似。

2. j = i

3. j > i

#include <bits/stdc++.h>
#define LL long long
#define int LL
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pcc pair<char,char>
#define x first
#define y second
#define inf 1e18
#define kanm7 ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 3010, mod = 1e9 + 7;
int dp[N][N], l[N], r[N];
int n;

void solve() {
    cin >> n;
    vector<int> a(n + 10);
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= n; i ++) {
        l[i] = 0, r[i] = n + 1;
        for(int j = i; j; j --) {
            if(a[j] < a[i]) {
                l[i] = j;
                break;
            }
        }
        for(int j = i; j <= n; j ++) {
            if(a[j] < a[i]) {
                r[i] = j;
                break;
            }
        }
        // cout << i << ' ' << l[i] << ' ' << r[i] << endl;
    }
    for(int i = 1; i <= n; i ++) {
        if(l[i] < 1) {
            dp[1][i] = 1;
            // cout << i << endl;
        }
    }
    for(int i = 2; i <= n; i ++) {
        for(int j = 1; j <= n; j ++) {
            if(j < i) {
                if(l[j] < i && i < r[j]) {
                    for(int x = 1; x <= n; x ++) {
                        if(x <= j && a[x] <= a[j]) {
                            dp[i][j] += dp[i - 1][x];
                            // cout << 1 << ' ' << j << ' ' << x << ' ' << dp[i - 1][x] << endl;
                            dp[i][j] %= mod;
                        }
                    }
                }
            }
            if(j == i) {
                if(l[j] < i && i < r[j]) {
                    for(int x = 1; x <= n; x ++) {
                        if(x <= i) {
                            dp[i][j] += dp[i - 1][x];
                            // cout << 2 << ' ' << j << ' ' << x << ' ' << dp[i - 1][x] << endl;
                            dp[i][j] %= mod;
                        }
                    }
                }
            }
            if(j > i) {
                if(l[j] < i && i < r[j]) {
                    for(int x = 1; x <= n; x ++) {
                        if(x <= i) {
                            dp[i][j] += dp[i - 1][x];
                            dp[i][j] %= mod;
                        } else if(x <= j && a[x] >= a[j]) {
                            dp[i][j] += dp[i - 1][x];
                            dp[i][j] %= mod;
                        }
                    }
                }
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= n; i ++) {
        ans += dp[n][i];
        // cout << i << ' ' << dp[n][i] << endl;
        ans %= mod;
    }
    cout << ans << endl;
}

signed main() { 
    kanm7;
    int T = 1;
    // cin >> T;
    while(T --) {
        solve();
    }
    return 0;
}   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kanm7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值