题解:B3903 [NICA #3] 星空(Hard Version)

这边是题目传送门喵!

题意分析

题干比较明确了,这里来讨论这个题的本质。

首先可以看到可行的排列很多很多,难以列举。我们来考虑哪些排列是不可行的:

  1. 如果一个数 \(a_i\) 已经超过或达到\(x\),那么它再加上任何一个数显然都会超过 \(x\)。由此容易证明:\(\exist a_i \geq x\) 时,一定不存在可行方案。注意\(a_i = x\) 时就会不存在可行方案,这里是要取等的。

  2. 还有一个关键的值是 \(\frac{x}{2}\)。对于两个超过 \(\frac{x}{2}\)\(a_i,a_j\) 相加是会超过 \(x\) 的。但是由于 \(\forall a_i =2^k < x (k \in \mathbb{N})\) 只会出现一个 \(k\) 满足 \(\frac{x}{2} \leq 2^k < x\),这里将等于这个值的数称为一号数

  3. 回到样例:

     6 46
     4 8 8 16 32 32
    

    这里的一号数就是在 \([23,46)\) 这个范围内的 \(2^k\),即 \(32\)。由第二条,两个 \(32\) 不可以挨着,但是我们发现 \(16 + 32 = 48 > 46\) 同样存在问题。因此,若 \(2^k + 2^p > x (p < k)\),则将 \(2^p\) 归为二号数。这一类数具有的性质是 \(x - 2^k < 2^p < 2^k\)。例如样例中二号数就是 \(16\)

  4. 除此之外的数,因为太小所以对结果影响不大,称为三号数以便于讨论。

那么根据以上的分类,可以对应地可以得出对可行方案的限制:

  1. 不存在 \(a_i \geq x\)
  2. 没有两个相邻的一号数;
  3. 没有一号数与二号数相邻;
  4. 二号数之间可以相邻。

思路

那把题意这么一理清楚就变成了一个组合问题了。首先将所有的数分类,就类似于将小球染成不同颜色。然后根据规则排列小球。

对于第一条限制输入的时候判一下无解就可以了。

我的第一想法是,将一号数视为挡板,然后每两个挡板之间都必须要有三号数。但是这样对于二号数的约束就比较难以实现,于是考虑将二号数作为挡板,然后对于每一个一号数都要用两个三号数包起来。想了想比较复杂,遂将三号数作为挡板,这样问题就简单了。

将三号数作为挡板,两个挡板之间的空中可以放一个一号数或若干个二号数,也可以放滚木。

然后就变成一个纯粹的排列组合问题了。

假设记录的三种数字个数分别为 \(cnt_1,cnt_2,cnt_3\)

首先考虑无解的情况。\(cnt_3\) 个三号数,算上两端,可以组成 \(cnt_3 + 1\) 个空。这个时候,如果没有二号数,则 \(cnt_1 > cnt_3 + 1\) 时无解;如果有二号数,则二号数至少占据一个空,\(cnt_1 + 1 > cnt_3 + 1\) 时无解。这时答案为 \(0\)。显然除此之外都是有解的情况。

其次考虑任意排列均可以的情况。显然,不存在一号数时任意排列均可;只存在一个一号数时,若不存在二号数也是任意排列均可。这时答案为 \(n !\)

最后则是一般情况。我将这整个过程拆分为以下几步:

  1. 放三号数:挡板的放置方案有 \(cnt_3 !\) 种。

  2. 放一号数:将一号数任意填入 \(cnt_3 + 1\) 个空中,有 \(P_{cnt_3 + 1}^{cnt_1}\) 种方案。设 \(s = cnt_3 + 1 - cnt_1\),则 \(P_{cnt_3 + 1}^{cnt_1} = \frac{(cnt_3 + 1)!}{s!}\)

  3. 放二号数:剩下的有 \(cnt_3 + 1 - cnt_1 = s\) 个空,我们将剩下的抽离出来,视为 \(s - 1\) 个挡板与 \(cnt_2\) 个二号数。接下来说的挡板是这 \(s - 1\) 个挡板。将二号数任意排列后用挡板挡开,则将二号数和挡板都视为不同的球进行排列即可。但是由于第一步将挡板的方案数计算过了,因此这里挡板应该视为是一样的,那干脆将挡板视为滚木,那么方案数为 \(P_{s-1+cnt_2}^{cnt_2} = \frac{(s-1+cnt_2)!}{(s-1)!}\)

这就算是放完了。由于这三步分别独立应使用乘法原理,则答案为 \(cnt_3 ! \times \frac{(cnt_3 + 1)!}{s!} \times \frac{(s-1+cnt_2)!}{(s-1)!}\)。显而易见,这里所有的数字至少都应该 小于 \(2n\) 的。因此我们先预处理 \(0\)\(n\) 的阶乘和阶乘的逆元,然后直接计算就可以了。复杂度为 \(\Theta (n)\)

代码

何意味。

有几个代码实现上的细节问题,梳理一下:

  1. 如果你和上面一样的分数:\(x\) 就是 \(2\) 的整数次幂,那么 \(a_i = x\) 时无解,\(\frac{x}{2} + \frac{x}{2}\) 不会超过 \(x\),所以此时不存在一号数;
  2. 如果你错最后一个测试点:如果 \(cnt_2 = 0\),则 \(P_{s-1+cnt_2}^{cnt_2} = 1\) 要特判,否则会越界。

说完了。

#include<bits/stdc++.h>
#define ll long long
#define db double
#define ls u<<1
#define rs u<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N=200005;
const ll mod=1e9+7;
const ll inf=1e18;

int n,cnt1,cnt2,cnt3;
ll x,a1,a;
ll fac[N],inv[N];

ll qpow(ll x,ll k){
    ll res=1ll;
    while(k){
        if(k&1)res=res*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return res;
}

int main(){
    ios;cin>>n>>x;

    // 求一号数的值
    a1=1ll;
    while(a1*2<x)a1*=2;
    if(a1*2==x)a1=-1; // 没有一号数

    for(int i=1;i<=n;i++){
        cin>>a;
        if(a>=x){ // 无解
            cout<<"0\n";
            return 0;
        }
        if(a==a1)cnt1++;
        if(x-a1<a&&a<a1)cnt2++;
    }

    cnt3=n-cnt1-cnt2; // 将这种球视为挡板
    if(cnt1+(cnt2!=0)>cnt3+1){ // 无解
        cout<<"0\n";
        return 0;
    }
    // 预处理
    fac[0]=1ll;
    for(ll i=1;i<=2*n+1;i++){
        fac[i]=1ll*fac[i-1]*i%mod;
    }
    inv[2*n+1]=qpow(fac[2*n+1],mod-2);
    for(ll i=2*n;i>=0;i--){
        inv[i]=1ll*inv[i+1]*(i+1)%mod;
    }

    if((cnt1==0)||(cnt1==1&&cnt2==0)){ // 如何摆放均可满足
        cout<<fac[n]<<'\n';
        return 0;
    }
    int s=cnt3+1-cnt1;
    ll ans=fac[cnt3]*fac[cnt3+1]%mod*inv[s]%mod;
    if(cnt2)ans=ans*fac[s-1+cnt2]%mod*inv[s-1]%mod;
    cout<<ans<<'\n';
    return 0;
}

完结撒花喵!

posted @ 2026-02-02 13:42  Circle_Table  阅读(8)  评论(0)    收藏  举报