AGC036F Square Constraints

AGC036F

有意思的,自然的。


刚开始有些地方没懂,想清楚之后记录一下。

原题可以转化一下,要求每个元素在半径分别为 \(n\)\(2n\) 的之间(包含圆上)。

注意到如果没有下界,只有上界是好做的,只要把上界从小到大排序,每一个位置上选择一个值都会使得后面每个位置的方案数 \(-1\)


有下界,考虑容斥,设 \(f_k\) 表示钦定 \(k\) 个位置一定不合法(小于下界)的方案数:

\[ans = \sum_{k = 0}^n (-1)^k \cdot f_k \]

考虑 \(f_k\) 如何计算。


\(i\) 的上界为 \(R_i\),下界为 \(L_i\)(若没有则为 \(0\))。

我们对于原序列中一个位置 \(i\),限制不大于它的位置 \(j\) 有哪些。

\(n \leq i < 2n\)

  • \(n \leq j < 2n\) 那么要求 \(R_j \leq R_i\)

  • \(0 \leq j < n\) 那么如果钦定了 \(j\) 不合法则要求 \(L_j - 1 \leq R_i\);如果没钦定则 \(R_j > R_i\), 不可能 。

\(0 \leq i < n\) 且被钦定不合法:

  • \(n \leq j < 2n\) 那么要求 \(R_j \leq L_i - 1\)

  • \(0 \leq j < n\) 那么如果钦定 \(j\) 不合法则要求 \(L_j - 1 \leq L_i - 1\);如果没钦定则不可能。

\(0 \leq i < n\) 且不被钦定:

  • \(n \leq j < 2n\) 那么一定满足;

  • \(0 \leq j < n\) 那么如果钦定即一定满足;如果没钦定则要求 \(R_j \leq R_i \Leftrightarrow L_j - 1 \leq L_i - 1\)

注意到所有的 \(i\)\(j\) 之间的限制都可以转化为相似形式,\(0 \leq i < n\)\(L_i - 1\) 为关键字,\(n \leq i < 2n\)\(R_i\) 为关键字,排序后进行 dp。

\(f_{i, j}\) 表示考虑前 \(i\) 个位置,钦定了 \(j\) 个不合法的方案数,\(r\) 表示前 \(i - 1\) 个位置中原属于 \([n,2n)\) 的位置个数,\(l\) 表示前 \(i - 1\) 个位置中原属于 \([0,n)\) 的位置个数。

如果 \(L_i = 0\) 那么 \(i\) 原来在 $ [ n,2n)$ 区间上:

\[f_{i,j} \longleftarrow f_{i, j} + f_{i-1, j} \cdot (R_i + 1 - r - j) \]

否则原来在 \([0,n)\) 区间上:

若钦定这一位:

\[f_{i,j} \longleftarrow f_{i, j} + f_{i-1, j} \cdot (L_i - r - (j - 1)) \]

若不钦定这一位:

\[f_{i,j} \longleftarrow f_{i, j} + f_{i-1, j-1} \cdot (R_i + 1 - k - n - (l - j)) \]

原理见前文。

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
const int maxn = 510;
const int inf = 1e9;
int n, mod;
int f[maxn][maxn];
vector<pair<int, int>> lim;
int Calc(int k) {
    memset(f, 0, sizeof(f));
    f[0][0] = 1;
    int lcnt = 0, rcnt = 0;
    for(int i = 1; i <= (n << 1); i++) {
        if(lim[i].second == 0) {
            for(int j = 0; j <= k; j++) {
                f[i][j] = (f[i][j] + (ll)f[i - 1][j] * (lim[i].first + 1 - rcnt - j) % mod) % mod;
            }
            rcnt++;
        } else {
            for(int j = 0; j <= k; j++) {
                f[i][j] = (f[i][j] + (ll)f[i - 1][j] * (lim[i].second + 1 - k - (lcnt - j) - n) % mod) % mod;
                if(j) f[i][j] = (f[i][j] + (ll)f[i - 1][j - 1] * (lim[i].first - j - rcnt + 2) % mod) % mod;
            }
            lcnt++;
        }
    }
    return f[n << 1][k];
}
void Init() {
    lim.emplace_back(-inf, -inf);
    for(int i = 0; i < n; i++) {
        int l = ceil(sqrt((ld)n * n - i * i));
        int r = min(2 * n - 1, (int)floor(sqrt(4 * n * n - i * i)));
        lim.emplace_back(l - 1, r);
    }
    for(int i = n; i < 2 * n; i++) {
        int r = min(2 * n - 1, (int)floor(sqrt(4 * n * n - i * i)));
        lim.emplace_back(r, 0);
    }
    sort(lim.begin(), lim.end());
}
int main() {
    cin >> n >> mod;
    Init();
    int ans = 0;
    for(int i = 0; i <= n; i++) {
        int del = Calc(i);
        if(i & 1) ans = (ans - del + mod) % mod;
        else ans = (ans + del) % mod;
    }
    cout << ans;
}
posted @ 2023-10-02 19:12  N2MENT  阅读(18)  评论(0)    收藏  举报