1 条题解

  • 0
    @ 2021-11-8 8:46:03

    考虑先找出一个子序列,满足每个数都大于等于前一个数的两倍,然后再去放其他数。

    先排序,我们从后向前放。

    dpidp_i 表示放到 ii 的答案是什么。

    可以枚举一个 jj 使 aj2×aia_j\ge 2\times a_i,然后考虑放 i+1..j1i+1..j-1 区间中的数。

    考虑现在取出的子序列是 ai,aj,aka_i,a_j,a_k ,那么 i+1..j1i+1..j-1 区间中的数有些可以放到 jj 的后面,有些只能放在 kk 的后面。

    考虑分成两类,记 gjg_j 表示最后面的一个小于 aj2\frac{a_j}{2} 的位置。

    分类,然后放就好了,细节比较多,讨论一下。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5005,mod=998244353;
    int n,a[N],f[N],dp[N],inv[N],g[N];
    int kuai(int a,int b){
        int ans=1;
        for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;
        return ans;
    }
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        if(a[n-1]*2>a[n])
        {
        	puts("0");
        	return 0;
    	}
        dp[n]=1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<i;j++){
                if(a[j]*2<=a[i])g[i]=j;
            }
        }
        f[1]=f[0]=1;
        for(int i=2;i<=n;i++)f[i]=1ll*f[i-1]*i%mod;//阶乘
        for(int i=0;i<=n;i++)inv[i]=kuai(f[i],mod-2);
        for(int i=n-1;i>=0;i--){
            for(int j=i+1;j<=n;j++){
                if(a[i]*2<=a[j]){
                	int num=1;
                	if(n-i-1>=n-g[j])num=1ll*num*f[n-i-1]%mod*inv[n-g[j]-1]%mod;//如果存在可以放到j后面的
    				if(n-2-g[j]>=n-j){//如果只能放在k后面
    					if(n-j)num=1ll*num*f[n-2-g[j]]%mod*inv[n-j-1]%mod;
    					else num=1ll*num*f[n-2-g[j]]%mod;
    				}
                    dp[i]=(dp[i]+1ll*dp[j]*num%mod)%mod;
                }
            }
        }
        printf("%d",dp[0]);//答案是dp[0],所有的数都放完了。
        return 0;
    }
    
    • 1

    信息

    ID
    569
    时间
    4000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    2
    已通过
    2
    上传者