P17693 砕月 题解

P17693 砕月

由于直接从填数的角度出发很复杂,而且题目问的是最终排列的数量,那就先考虑什么样的最终排列是合法的。

结论是,\(\forall i\in [1,n],v=a_i,x_v \le i \lor y_v \le \max_{j<i}a_j\) 是最终排列合法的充要条件。

证明:

1.充分性:直接按顺序填就是了。

2.必要性:当最终排列合法时,前面尚有空位的前缀 \(\max\) 亦小于,则必然也小于最终无空位的前缀最大值了。

所以我们把问题转化成了对满足要求的排列计数的问题。

考虑 DP,设 \(f_{i,j}\) 表示填了前 \(i\) 个位置,前缀最大值为 \(j\) 的方案数。转移显然分为更新最大值和不更新两种。

1.考虑不更新最大值,令 \(cnt_{i,j}\) 表示 \(x_k\le i\) 或者 \(v_k \le j\) 且 \(k<j\) 的数量,则 \(f_{i+1,j} \leftarrow f_{i,j}\times (cnt_{i+1,j}-i)\),因为前面选的 \(i\) 个数条件更加严格,所以一定在这 \(cnt_{i,j}\) 个数中。

2.更新了最大值,则 \(f_{i+1,k} \leftarrow f_{i,j}\),这个转移是 \(O(n^3)\) 的,无法通过。

考虑优化,更新最大值的 \(k\) 有两种情况,一是 \(x_k \le i+1\),则 \(f_{i+1,k} \leftarrow \sum_{j<k} f_{i,j}\)。二是 \(x_k >i+1 \land y_k \le k\),则 \(f_{i+1,k} \leftarrow \sum_{v_k \le j<k}f_{i,j}\),两个式子都可以前缀和优化。时间复杂度 \(O(n^2)\)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
int T,n;
const int N = 5e3+10,mod=998244353;
int x[N],y[N],f[N][N],cnt[N][N],s[N],pre[N];
inline void add(int &x,int y){
    x=(x+y>=mod)?x+y-mod:x+y;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>x[i];
        }
        for(int i=1;i<=n;i++){
            cin>>y[i];
        }
        for(int j=1;j<=n;j++){
            int cj=0;
            for(int i=0;i<=n;i++)s[i]=0;
            for(int v=1;v<=j;v++){
                if(y[v]<=j){
                    cj++;
                }else{
                    s[x[v]]++;
                }
            }
            for(int i=1;i<=n;i++){
                (s[i]+=s[i-1])%=mod;
                cnt[i][j]=(s[i]+cj)%mod;
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)f[i][j]=0;
        }
        for(int i=1;i<=n;i++){
            if(1>=x[i])f[1][i]=1;
            pre[i]=(pre[i-1]+f[1][i])%mod;
        }
        for(int i=1;i<n;i++){
            for(int j=i;j<=n;j++){
                add(f[i+1][j],(f[i][j]*(cnt[i+1][j]-i+mod)%mod)%mod);
            }
            for(int k=i+1;k<=n;k++){
                if(x[k]<=i+1){
                    add(f[i+1][k],pre[k-1]);
                }else{
                    add(f[i+1][k],(pre[k-1]-pre[y[k]-1]+mod)%mod);
                }
            }
            pre[0]=0;
            for(int j=1;j<=n;j++){
                pre[j]=(pre[j-1]+f[i+1][j])%mod;
            }
        }
        cout<<f[n][n]<<endl;
    }
    return 0;
}
posted @ 2026-10-19 15:39  farenara_sayowell  阅读(5)  评论(0)    收藏  举报