LibreOJ 6502 「雅礼集训 2018 Day4」Divide 题解 [ 蓝 ] [ 多维 DP ] [ 构造 ]

Divide

神仙题。

一个显然的想法是我们把人按照某种顺序排序,之后根据有序的性质进行 DP。但是你发现本题中“有序”这个性质根本用不上,我们需要找到另一种性质构造排列使其能够直接 DP。

注意到把人按照 \(w_i\) 排序后,若最后一个人和第一个人的和 \(\bm{w_n+w_1\ge m}\),那么对于 $\bm{\forall 1 \le i< n} $,都有 \(\bm{w_n + w_i\ge m}\)。这是个非常好进行 DP 的性质,即保证每个人都能与其前面的所有人决斗。但是我们发现有些人并不能满足这个性质。又发现当 \(w_n+w_1< m\) 的时候,对于 $\forall 1 < i\le n $,都有 \(w_1+w_i<m\)。这同样是一个非常好的性质,即保证每个人都不能与其前面的所有人决斗。发现这两个限制拼在一起就能构成全集,所以对原序列这样递归构造下去,我们就一定能得到目标排列。

于是定义 \(dp_{i, j}\) 表示前 \(i\) 个人中,有 \(j\) 个在 A 队的最大匹配与方案数,转移即可。时间复杂度 \(O(n^2)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
using pl = pair<int, ll>;
const int N = 2005, inf = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
int n, m, a[N], p[N], cnt;
void divide(int l, int r)
{
    if(l == r)
    {
        p[cnt--] = a[l];
        return;
    }
    if(a[l] + a[r] >= m)
    {
        p[cnt--] = a[r];
        divide(l, r - 1);
        return;
    }
    p[cnt--] = a[l];
    divide(l + 1, r);
}
pl dp[N][N];
void add(pl &x, pl y)
{
    if(x.fi < y.fi)
    {
        x = y;
        return;
    }
    else if(x.fi == y.fi)
    {
        x.se += y.se;
        if(x.se >= mod) x.se -= mod;
    }
}
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + n + 1);
    cnt = n;
    divide(1, n);
    for(int i = 0; i <= n; i++)
        for(int j = 0; j <= n; j++)
            dp[i][j] = {-inf, 0};
    dp[1][0] = dp[1][1] = {0, 1};
    for(int i = 2; i <= n; i++)
    {
        for(int j = 0; j <= i; j++)
        {
            if(p[i] + p[1] >= m)
            {
                if(j - 1 >= 0)
                {
                    pl res = dp[i - 1][j - 1];
                    res.fi += i - 1 - (j - 1);
                    add(dp[i][j], res);
                }
                pl res = dp[i - 1][j];
                res.fi += j;
                add(dp[i][j], res);
            }
            else
            {
                if(j - 1 >= 0) add(dp[i][j], dp[i - 1][j - 1]);
                add(dp[i][j], dp[i - 1][j]);
            }
        }
    }
    pl ans = {-1, 0};
    for(int i = 0; i <= n; i++)
        add(ans, dp[n][i]);
    cout << ans.fi << " " << ans.se;
    return 0;
}
posted @ 2025-11-18 15:38  KS_Fszha  阅读(5)  评论(0)    收藏  举报