洛谷 P8962 「WHOI-4」C yadiw. Slua, gassp, lhtubs. 题解

题目来源

Description

  • 对于长度为 i ∈ [ 1 , n ] i\in[1,n] i[1,n] 的一个 [ 1 , i ] [1,i] [1,i] 的无序排列,求能够通过二分的方法查询到第 k ∈ [ 1 , i ] k\in[1,i] k[1,i] 小的元素的序列个数。
  • 10 ≤ n ≤ 400 10\le n\le400 10n400,答案对给定的 2 ≤ p ≤ 998244353 2\le p\le998244353 2p998244353 取模( p p p 不一定为质数)。

Solution

首先我们考虑,若需要正确地找到第 k k k 小的元素(我们称之为 x x x),则对于每一个 M i d Mid Mid a M i d a_{Mid} aMid x x x 的大小关系是确定的。

换言之,若 x x x 位于位置 p p p,我们可以模拟二分的过程:

对于当前的 M i d Mid Mid,若 p < M i d p<Mid p<Mid 则必然 a M i d > x a_{Mid}>x aMid>x,若 p > M i d p>Mid p>Mid 则必然 a M i d < x a_{Mid}<x aMid<x,否则已经找到了 x x x

那么我们就能求出 a a a 数列中,必须小于 x x x 以及必须大于 x x x 的数的个数,分别记作 c n t 1 cnt_1 cnt1 c n t 2 cnt_2 cnt2,则大小关系与 x x x 不确定的个数为 n − c n t 1 − c n t 2 − 1 n-cnt_1-cnt_2-1 ncnt1cnt21(总数减小于减大于减等于的个数),其中 n n n 为数列长度。则满足此大小关系的数列的个数为: A x − 1 c n t 1 × A n − x c n t 2 × A n − c n t 1 − c n t 2 − 1 n − c n t 1 − c n t 2 − 1   m o d   p A_{x-1}^{cnt_1}\times A_{n-x}^{cnt_2}\times A_{n-cnt_1-cnt_2-1}^{n-cnt_1-cnt_2-1}\bmod p Ax1cnt1×Anxcnt2×Ancnt1cnt21ncnt1cnt21modp

则我们可以设立三层循环:第一层循环 i ∈ [ 1 , n ] i\in[1,n] i[1,n] 表示数列长度,第二层循环 j ∈ [ 1 , i ] j\in[1,i] j[1,i] 表示需要找到第 j j j 大的元素,第三层循环 k ∈ [ 1 , i ] k\in[1,i] k[1,i] 表示第 j j j 大元素位于位置 k k k 时的方案数。若设 a n s ( i , j ) ans_{(i,j)} ans(i,j) 为输出中第 i i i 行第 j j j 列,则
a n s ( i , j ) = ( ∑ k = 1 i A j − 1 c n t 1 × A i − j c n t 2 × A i − c n t 1 − c n t 2 − 1 i − c n t 1 − c n t 2 − 1 )   m o d   p ans_{(i,j)}=\Big(\sum_{k=1}^iA_{j-1}^{cnt_1}\times A_{i-j}^{cnt_2}\times A_{i-cnt_1-cnt_2-1}^{i-cnt_1-cnt_2-1}\Big)\bmod p ans(i,j)=(k=1iAj1cnt1×Aijcnt2×Aicnt1cnt21icnt1cnt21)modp
若我们暴力求 A A A,那么复杂度会达到 O ( n 4 log ⁡ n ) O(n^4\log n) O(n4logn),显然不行;那么我们可以 O ( n 2 ) O(n^2) O(n2) 预处理出 ∀ i ∈ [ 1 , n ] , j ∈ [ 1 , i ] \forall i\in[1,n],j\in[1,i] i[1,n],j[1,i] C i j C_i^j Cij 的值, O ( n ) O(n) O(n) 预处理出 ∀ i ∈ [ 1 , n ] \forall i\in[1,n] i[1,n] i ! i! i! 的值,再结合排列数和阶乘 O ( n 2 ) O(n^2) O(n2) 预处理出 A i j = C i j × j ! A_i^j=C_i^j\times j! Aij=Cij×j! 降低复杂度即可。并且可以 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn) 预处理出 c n t 1 , c n t 2 cnt_1,cnt_2 cnt1,cnt2 ,最终复杂度为 O ( n 3 ) O(n^3) O(n3)

Code

#include <bits/stdc++.h>
using namespace std;
int p,n,C[500][500],frac[500]={1},A[500][500],cnt1[500][500],cnt2[500][500]; // cnt1表示<的个数 cnt2表示>的个数
int main(){
    scanf("%d%d",&p,&n);
    for (int i=1;i<=n;i++) frac[i]=1ll*frac[i-1]*i%p; // 预处理阶乘 
    for (int i=0;i<=n;i++) C[i][0]=C[i][i]=1;
    for (int i=1;i<=n;i++) for (int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%p; // 预处理组合数 
    for (int i=0;i<=n;i++) for (int j=0;j<=i;j++) A[i][j]=1ll*C[i][j]*frac[j]%p; // 组合数*阶乘=排列数 
    for (int i=1;i<=n;i++) // 预处理 cnt1,cnt2
    	for (int k=1;k<=i;k++){
    		int L=1,R=i;
			while (L<R){
				int Mid=(L+R)>>1;
				if (k==Mid) break;
				if (k<Mid) cnt2[i][k]++,R=Mid-1;
				else cnt1[i][k]++,L=Mid+1;
            }
		}
    for (int i=1;i<=n;i++){
        for (int j=1;j<=i;j++){
            int cnt=0;
			// i个元素中,j位于k时能够找到j的方案数
            for (int k=1;k<=i;k++) cnt=(cnt+1ll*A[j-1][cnt1[i][k]]*A[i-j][cnt2[i][k]]%p*A[i-cnt1[i][k]-cnt2[i][k]-1][i-cnt1[i][k]-cnt2[i][k]-1]%p)%p;
            printf("%d ",cnt);
        }
        puts("");
    }
    return 0;
}
  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值