• AGC055F Creative Splitting【双射转化,dp】


    给定正整数 \(n,k\) 和质数 \(p\),定义长为 \(k\) 的正整数序列 \(\{a_1,\cdots,a_k\}\) 是好的 \(\iff\forall i\in[1,k],a_i\le i\)

    对每个 \(\text{pos}\in[1,nk],\text{val}\in[1,k]\),求使得 \(b_{\text{pos}}=\text{val}\) 且可以划分为 \(n\) 个长为 \(k\) 的好子序列的长为 \(nk\) 的正整数序列 \(\{b_1,\cdots,b_{nk}\}\) 的数量 \(\bmod p\)

    \(n,k\le 20\)\(10^8<p<10^9\)


    考虑判断一个序列是不是 amazing 的,直接从后往前贪心放,放到能放的子序列里面位置最前的。

    设每个子序列剩余位置为 \(c_1,\cdots,c_n\),初始时全为 \(k\),贪心过程即找到不小于 \(b_i\) 的第一个位置然后减 \(1\)

    考虑进行一个转置:将剩余位置排成一个矩形,维护左边界,就转化为 \(k\) 个不同的球在数轴上,初始位置全为 \(0\),每次操作选择一个位置不在 \(n\) 的球,将其移到下一个位置。

    \(b_{\text{pos}}=\text{val}\) 的限制即为第 \(nk-\text{pos}+1\) 次操作将位置为 \(x\) 的球放到 \(x+1\),其中 \(\text{pre}_x<\text{val}\le\text{pre}_{x+1}\)\(\text{pre}_x\) 表示位置 \(0,1,\cdots,x-1\) 上的球的个数。

    设这次操作之前第 \(i\) 个球的位置为 \(p_i\),则 \(p_1+\cdots+p_k=\text{pos}-1\),方案数为 \(\dfrac{(i-1)!(nk-i)!(n-p_?)}{\prod p_j!(n-p_j)!}\),其中 \(p_?\) 表示这次操作的球的位置。

    然后就可以 dp 了,先枚举 \(\text{val}\),设 \(\text{cnt}_i\) 表示有多少个 \(p_j\)\(i\),从小到大枚举 \(i\),将 \(\text{pre}_i=\sum\limits_{j<i}\text{cnt}_j\)\(\sum\limits_{j<i}j\cdot\text{cnt}_j\) 记到状态里,就可以知道 \(p_?\) 是哪一个了,用 dp 数组即可算出每个 \(\text{pos}\) 的答案。时间复杂度 \(O(n^2k^4)\)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 21, M = N*N;
    int n, k, mod, pw[N][N], fac[M], inv[M], ans[M][N], f[N][M], g[N][M];
    int ksm(int a, int b){
        int res = 1;
        for(;b;b >>= 1, a = (LL)a * a % mod)
            if(b & 1) res = (LL)res * a % mod;
        return res;
    }
    int main(){
        ios::sync_with_stdio(false);
        cin >> n >> k >> mod; *fac = 1;
        for(int i = 1;i < M;++ i) fac[i] = (LL)fac[i-1] * i % mod;
        inv[M-1] = ksm(fac[M-1], mod-2);
        for(int i = M-1;i;-- i) inv[i-1] = (LL)inv[i] * i % mod;
        for(int i = 0;i < N;++ i)
            for(int j = pw[i][0] = 1;j < N;++ j)
                pw[i][j] = (LL)pw[i][j-1] * inv[i] % mod;
        for(int val = 1;val <= k;++ val){
            memset(f, 0, sizeof f); **f = 1;
            for(int i = 0;i < n;++ i){
                memset(g, 0, sizeof g);
                for(int j = 0;j <= k;++ j)
                    for(int l = 0;l <= j*(i-1);++ l) if(f[j][l])
                        for(int t = 0;t <= k-j;++ t)
                            if(j < val && val <= j+t) g[j+t][l+i*t] = (g[j+t][l+i*t] + LL(n-i) * f[j][l] % mod * inv[t] % mod * pw[i][t] % mod * pw[n-i][t]) % mod;
                            else g[j+t][l+i*t] = (g[j+t][l+i*t] + (LL)f[j][l] * inv[t] % mod * pw[i][t] % mod * pw[n-i][t]) % mod;
                memcpy(f, g, sizeof f);
            }
            for(int i = 1;i <= n*k;++ i)
                for(int j = val;j <= k;++ j)
                    if(i-1 >= (k-j)*n) ans[n*k-i+1][val] = (ans[n*k-i+1][val] + (LL)f[j][i-1-(k-j)*n] * inv[k-j] % mod * pw[n][k-j]) % mod;
        }
        for(int i = 1;i <= n*k;++ i)
            for(int j = 1;j <= k;++ j)
                printf("%lld%c", (LL)ans[i][j]*fac[k]%mod*fac[i-1]%mod*fac[n*k-i]%mod, " \n"[j==k]);
    }
    
  • 相关阅读:
    学习进度条
    阅读《实例化需求》10-12章有感
    学习进度条
    阅读《实例化需求》7–9章有感
    学习进度条
    软件需求与分析课堂讨论一
    课程引言课后作业1
    MVC实例应用模式
    MVC模式介绍
    二十三种设计模式
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/15715570.html
Copyright © 2020-2023  润新知