2024CCPC网络预选赛 I. 找行李 【DP】

传送门:https://codeforces.com/gym/105336

思路

(思路参考于官方题解,对一些地方进行详细说明)

令 g ( d ) g(d) g(d) 表示答案大于等于 d d d 的方案数,那么很显然答案就是: ∑ d = 1 ∞ g ( d ) \sum_{d = 1}^{\infty} g(d) ∑d=1∞g(d)

这是因为:对于一个答案 d d d,它会在 [ 1 , d ] [1, d] [1,d] 各算一次

考虑枚举 d d d,那么对于每个人 i i i,只能选择 a i − d a_i - d ai−d 及之前的行李配对给他

如果我们对每个人的位置排序,我们可以发现每个人左边的行李是确定的,且 i i i 可选的行李一定是 i + 1 i + 1 i+1 的可选行李的子集 ,那么这里我们就可以转化成一个很经典的 d p dp dp

定义 d p i , j dp_{i, j} dpi,j 为:前 i i i 个人,选了 j j j 个物品的方案数,假设第 i i i 个人可选的行李数量为 k k k 个,他只能选择一个或者不选

那么我们有转移:

d p i , j = d p i − 1 , j + d p i − 1 , j − 1 × ( k − ( j − 1 ) ) dp_{i,j} = dp_{i -1,j} + dp_{i - 1, j - 1} \times (k - (j - 1)) dpi,j=dpi−1,j+dpi−1,j−1×(k−(j−1))

后面意思就是, k k k 个物品已经选了 j − 1 j - 1 j−1 个,还有 k − j + 1 k - j + 1 k−j+1 个可选,从中选一个即可。

这里我们可以看成将没选的按照编号 排个序,就可以不重不漏地选择了,共有 k − j + 1 k - j + 1 k−j+1 个物品可选

上述 k k k 我们可以使用前缀和快速得到

边界设置 d p 0 , 0 = 1 dp_{0, 0} = 1 dp0,0=1 即可

时间复杂度: O ( n 3 ) O(n ^ 3) O(n3)

cpp 复制代码
#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;

const int INF=0x3f3f3f3f;
const long long INFLL=1e18;

typedef long long ll;

template<class T>
constexpr T power(T a, ll b){
    T res = 1;
    while(b){
        if(b&1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出
    ll res = a * b - ll(1.L * a * b / mod) * mod;
    res %= mod;
    if(res < 0) res += mod; //误差
    return res;
}

template<ll P>
struct MLL{
    ll x;
    constexpr MLL() = default;
    constexpr MLL(ll x) : x(norm(x % getMod())) {}

    static ll Mod;
    constexpr static ll getMod(){
       if(P > 0) return P;
       return Mod;
    }

    constexpr static void setMod(int _Mod){
       Mod = _Mod;
    }
    constexpr ll norm(ll x) const{
       if(x < 0){
           x += getMod();
       }
       if(x >= getMod()){
           x -= getMod();
       }
       return x;
    }
    constexpr ll val() const{
       return x;
    }
    explicit constexpr operator ll() const{ 
       return x; //将结构体显示转换为ll类型: ll res = static_cast<ll>(OBJ)
    }
    constexpr MLL operator -() const{ //负号,等价于加上Mod
       MLL res;
       res.x = norm(getMod() - x);
       return res;
    }
    constexpr MLL inv() const{
       assert(x != 0);
       return power(*this, getMod() - 2); //用费马小定理求逆
    }
    constexpr MLL& operator *= (MLL rhs) & { //& 表示"this"指针不能指向一个临时对象或const对象
       x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用
       return *this;
    }
    constexpr MLL& operator += (MLL rhs) & {
       x = norm(x + rhs.x);
       return *this;
    }
    constexpr MLL& operator -= (MLL rhs) & {
       x = norm(x - rhs.x);
       return *this;
    }
    constexpr MLL& operator /= (MLL rhs) & {
       return *this *= rhs.inv();
    }
    friend constexpr MLL operator * (MLL lhs, MLL rhs){
       MLL res = lhs;
       res *= rhs;
       return res;
    }
    friend constexpr MLL operator + (MLL lhs, MLL rhs){
       MLL res = lhs;
       res += rhs;
       return res;
    }
    friend constexpr MLL operator - (MLL lhs, MLL rhs){
       MLL res = lhs;
       res -= rhs;
       return res;
    }
    friend constexpr MLL operator / (MLL lhs, MLL rhs){
       MLL res = lhs;
       res /= rhs;
       return res;
    }
    friend constexpr std::istream& operator >> (std::istream& is, MLL& a){
       ll v;
       is >> v;
       a = MLL(v);
       return is;
    }
    friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){
       return os << a.val();
    }
    friend constexpr bool operator == (MLL lhs, MLL rhs){
       return lhs.val() == rhs.val();
    }
    friend constexpr bool operator != (MLL lhs, MLL rhs){
       return lhs.val() != rhs.val();
    }
};

const ll mod = 998244353;
using Z = MLL<mod>;
template<>
ll MLL<0ll>::Mod = 998244353;
//using Z = MLL<0ll>;

const int N = 505;

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n), b(m);
    std::vector<int> cnt(N, 0);
    fore(i, 0, n){
        std::cin >> a[i];
        ++cnt[a[i]];
    }
    fore(i, 0, m) std::cin >> b[i];
    std::sort(ALL(b));
    fore(i, 1, N) cnt[i] += cnt[i - 1];

    Z ans = 0;
    fore(d, 1, N - 4){
        std::vector<Z> dp(m + 1);
        dp[0] = 1;
        for(auto p : b){
            auto f = dp;
            int num = (p - d >= 0 ? cnt[p - d] : 0);
            fore(j, 1, std::min(num, m) + 1) dp[j] += f[j - 1] * (num - j + 1);
        }
        fore(k, 1, m + 1) ans += dp[k];
    }

    std::cout << ans << endl;
    
    return 0;
}
相关推荐
XH华4 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_4 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子4 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡5 小时前
滑动窗口 + 算法复习
数据结构·算法
Lenyiin5 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码5 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
scan7245 小时前
LILAC采样算法
人工智能·算法·机器学习
菌菌的快乐生活5 小时前
理解支持向量机
算法·机器学习·支持向量机
大山同学5 小时前
第三章线性判别函数(二)
线性代数·算法·机器学习