loading...

[AGC030F]Permutation and Minimum

Description

给定一个长为 \(2n\) 的序列 \(A\),其中 \(A\) 的每一位要么是 \(-1\),要么是 \(1\sim 2n\) 的一个数。将每个 \(A_i=-1\) 替换为 \(1 \sim 2n\) 的某个数,使 \(A\) 成为一个长 \(2n\) 的排列。令长为 \(n\) 的序列 \(B\)\(B_i=\min \{A_{2i-1},A_{2i}\}\),求可能的 \(B\) 序列个数。

Solution

可以将原序列分为 \(n\) 个组,组内下标为 \(2i-1,2i\)

那么有 3 种情况:

  1. 两个数分别为 \(-1,-1\)
  2. 其中一个为 \(-1\),另一个的值是确定的。
  3. 两个数的值都是确定的。

第 3 种情况是朴素的,因为该组生成的 \(B_i\) 是确定的,可以考虑直接排除掉。

考虑剩下两种情况。由于 \(\min\) 的存在,很容易想到从大到小枚举填哪个值。我们发现原序列的各组顺序没有影响,因此钦定 \(B\) 从大到小排序。考虑 dp,每次转移时我们需要知道:

满足情况 1 的组个数,满足情况 2 的组个数。

如果继续这样思考仍然是可做的,但是不妨换一个角度。我们只关心 \(-1\) 的个数和 \(A_i\neq -1\) 的数的个数。首先每个 \(A_i\neq-1\) 对应一个情况 2 的组。那么相当于一个匹配问题,每个 \(A_i=-1\) 可以对应一个 \(A_j=-1\)\(A_{j}\neq -1\),这样匹配完成了过后,等价于原序列中的情况 1. 和 2. 的组。

那么原问题转化为(先删掉所有情况 3 的组):

有若干个 \(-1\) 和值域在 \(1 \sim 2n\) 的数。每个 \(\neq -1\) 的数都必须匹配一个 \(-1\)\(-1\) 可以任意匹配。

匹配完成后,将 \(-1\) 替换为 \(1 \sim 2n\) 的值,要求所有数互不相同,求每对匹配的最小值序列个数。

定义 \(f_{i,j,k}\) 表示考虑填一个值 \(i\),前面有 \(j\)\(-1\)\(k\)\(\neq -1\) 的方案数:

  1. 原序列存在 \(i\)
    • 选择匹配一个 \(-1\)\(f_{i,j,k} \to f_{i-1,j-1,k}\)
    • 选择不匹配,与后面的匹配:\(f_{i,j,k} \to f_{i-1,j,k+1}\)
  2. 原序列不存在 \(i\):(当前这个值一定是从 \(-1\) 变过来的)
    • 选择匹配一个 \(-1\)\(f_{i,j,k} \to f_{i-1,j-1,k}\)
    • 选择匹配一个 \(\neq -1\)\(\neq-1\) 的原序列对应组是确定的,因此互不相同,乘一个系数):\(f_{i,j,k}\times k \to f_{i,j,k-1}\)
    • 不匹配,与后面的匹:\(f_{i,j,k} \to f_{i,j+1,k}\)

初值:\(f_{2n,0,0}\),答案:\(f_{0,0,0} \times c!\)\(c\) 是原序列 \((-1,-1)\) 组的个数,由于刚刚没考虑他们顺序,需要乘一个系数。

code

Click to View Code
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define per(i, r, l) for (int i = r; i >= l; --i)
#define int long long
#define LL long long
#define ull unsigned long long
#define FRE(p) freopen(#p".in", "r", stdin),freopen(#p".out","w",stdout)
#define DBG(p) freopen(#p".in", "r", stdin),freopen("my.out","w",stdout)
#define FASTIO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N = 605, M = 505, mod = 1e9+7;
int n, a[N], f[N][N][N];
int bj[N];
inline void cadd(int& x, int y) {
    x = (x+y)%mod;
}
int fac[N];
signed main() {
#ifdef LOCAL
    FRE(bomb);
#endif
    FASTIO;
    cin >> n;
    fac[0] = 1;
    rep(i, 1, (n<<1)) cin >> a[i], bj[max(0ll, a[i])]=1, fac[i] = fac[i-1]*i%mod;
    int tmp = 0;
    rep(i, 1, n) {
        tmp += a[i<<1]==-1&&a[(i<<1)-1]==-1;
        if (a[i<<1]>0&&a[(i<<1)-1]>0) {
            bj[a[i<<1]] = 2, bj[a[(i<<1)-1]] = 2;
        }
    }
    f[n<<1][0][0] = 1;
    int cnt = 0;
    per(i, (n<<1), 1) {
        rep(j, 0, cnt) {
            rep(k, 0, (n<<1)-i-cnt) {
                if (bj[i]==2) cadd(f[i-1][j][k], f[i][j][k]);
                else if (bj[i]==1) {
                    cadd(f[i-1][j][k+1], f[i][j][k]);
                    if (j > 0) cadd(f[i-1][j-1][k], f[i][j][k]);
                } else {
                    cadd(f[i-1][j+1][k], f[i][j][k]);
                    if (j > 0) cadd(f[i-1][j-1][k], f[i][j][k]);
                    if (k > 0) cadd(f[i-1][j][k-1], k*f[i][j][k]);
                }
            }
        }
        cnt += bj[i]==0;
    }
    cout << f[0][0][0]*fac[tmp]%mod << '\n';
	return 0;
}
/*
morning: *******************
afternoon: *********
*/
posted @ 2025-11-16 15:37  goldspade  阅读(0)  评论(0)    收藏  举报