[QOJ14530] 找行李

前言:

感觉再不写就真的记不起来了。

题目描述:

有一个长条形的传送带,上面有 \(n\) 个行李,第 \(i\) 个行李在 \(a_{i}\) 处,有 \(m\) 个人在等行李,第 \(i\) 个人在位置 \(b_{i}\),传送带每过一秒会把所有行李向右移动一个单位的长度,即所有 \(a_{i}+1\),对于一个人来讲他的行李只可能在自己左侧,或者没有,一个人最多只有一个行李,问在所有分配行李的方案中,每种方案最早有人拿到行李的时间的和是多少?

解题思路:

注意到 \(n,m \leq 500\),所以我们可以先枚举最早有人拿到行李的时间 \(t\),然后计算有多少方案满足这个限制。但是这有一个问题,那就是我们很难直接统计最早时间刚好为 \(t\) 的方案数,然后这里有一个比较套路的想法,那就是转换成计算最早时间 \(\leq t\) 或者 \(\geq t\) 的方案,然后简单容斥即可。

简单分析可以发现,选择统计 \(\geq t\) 的方案数比较可行,这样相当于我们限制了所有人取得行李只能是一个前缀。然后就能得出一个 dp 状态,设计 \(f_{i,j}\) 表示做到第 \(i\) 个人先前的所有人选了 \(j\) 个行李的方案数,这种状态下第 \(i+1\) 人能选的行李个数是可以确定的,然后直接转移即可。

代码实现:

#include<bits/stdc++.h>
using namespace std;
const int N = 510, mod = 998244353;
int n, m, a[N], b[N], f[N][N], ans;
void add(int &x, int y){x = (x + y) % mod;}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= m; i++) cin >> b[i];
    sort(a + 1, a + 1 + n), sort(b + 1, b + 1 + m);
    for(int x = b[m] - a[1], sum = 0, lst = 0; x >= 1; x--){
        memset(f, 0, sizeof f), f[0][0] = 1, lst = sum, sum = 0;
        for(int i = 1, pos = 0; i <= m; i++){
            while(pos < n && b[i] - a[pos + 1] >= x) pos++;
            for(int j = 0; j <= pos; j++){
                add(f[i][j], f[i - 1][j]);
                if(j) add(f[i][j], 1ll * f[i - 1][j - 1] * (pos - j + 1) % mod);
            }
        }
        for(int j = 1; j <= m; j++) add(sum, f[m][j]);
        add(ans, 1ll * x * ((sum - lst + mod) % mod) % mod);
    }
    cout << ans << endl;
    return 0;
}//想给这题写个题解,能想起来吗?qwq
posted @ 2025-11-12 08:29  _huangweiliang  阅读(7)  评论(0)    收藏  举报