题目链接:传送门
题目思路:
题目思路参考于博客:&*^*&(
题目是关于前缀最大值的,那么不妨先将a数组排序,定义dp(i,j) 表示长度为 i 且最大元素为 aj 的合法排列个数;
根据排序后的单调性,预处理出 posi ,posi 的值是满足 aj * 2 <= ai 的最大的 j ;
所谓合法,即满足下式:(p数组是1-n的排列)
现在考虑dp(i,j)的转移:
首先排列的最大值为aj ,那么其他 i -1 个项是一定 <= aj / 2 的,且保证合法,
1)dp(i,j) <- dp(i-1,j) * max( 0 , posj - i + 2 ) ;
如果是前 i-1 项 的最大元素已经是 aj 了,那么第 i 个位置 能填写的数字个数为 posj - i +2 个(前面有一个aj 和 i-2 个 ak ,k<=posj ,这个位置就只剩下 posj - (i-2) 个数字可填 );
2)dp(i,j) <- Σdp(i-1,k) , k≥1 && k≤posj ;(前缀和优化)
如果前 i-1 项的最大元素不为aj ,由第 i 个位置 加入 aj 将最大值更新为 aj ,那么前 i-1 项的最大值为 ak , k≥1 && k≤posj ;
显然 ,dp(1,i) = 1 ,则将dp(1,i) 作为 初始状态,递推填表即可。
代码:
1 #include<bits/stdc++.h> 2 #pragma GCC optimize(2) 3 using namespace std; 4 typedef long long LL; 5 typedef unsigned long long uLL; 6 typedef pair<int,int> pii; 7 typedef pair<LL,LL> pLL; 8 typedef pair<double,double> pdd; 9 const int N=5e3+5; 10 const int M=5e3+5; 11 const int inf=1e8; 12 const LL mod=998244353; 13 const double eps=1e-8; 14 const long double pi=acos(-1.0L); 15 #define ls (i<<1) 16 #define rs (i<<1|1) 17 #define fi first 18 #define se second 19 #define pb push_back 20 #define eb emplace_back 21 #define mk make_pair 22 #define mem(a,b) memset(a,b,sizeof(a)) 23 LL read() 24 { 25 LL x=0,t=1; 26 char ch; 27 while(!isdigit(ch=getchar())) if(ch=='-') t=-1; 28 while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); } 29 return x*t; 30 31 } 32 LL a[N],dp[N][N],pos[N],sum[N]; 33 int main() 34 { 35 int n=read(); 36 for(int i=1;i<=n;i++) a[i]=read(); 37 sort(a+1,a+n+1); 38 int l=0; 39 for(int i=1;i<=n;i++){ 40 while(a[l+1]*2<=a[i]) l++; 41 pos[i]=l; 42 } 43 for(int i=1;i<=n;i++) dp[1][i]=1; 44 for(int i=2;i<=n;i++) 45 { 46 for(int j=1;j<=n;j++) sum[j]=(sum[j-1]+dp[i-1][j])%mod; 47 for(int j=1;j<=n;j++) 48 { 49 dp[i][j]=sum[pos[j]]; 50 dp[i][j]+=dp[i-1][j]*max(0LL,pos[j]-i+2); 51 dp[i][j]%=mod; 52 } 53 } 54 printf("%lld ",dp[n][n]); 55 return 0; 56 }