题解:B3903 [NICA #3] 星空(Hard Version)
题意分析
题干比较明确了,这里来讨论这个题的本质。
首先可以看到可行的排列很多很多,难以列举。我们来考虑哪些排列是不可行的:
-
如果一个数 \(a_i\) 已经超过或达到了 \(x\),那么它再加上任何一个数显然都会超过 \(x\)。由此容易证明:\(\exist a_i \geq x\) 时,一定不存在可行方案。注意是 \(a_i = x\) 时就会不存在可行方案,这里是要取等的。
-
还有一个关键的值是 \(\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\),这里将等于这个值的数称为一号数。
-
回到样例:
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\)。
-
除此之外的数,因为太小所以对结果影响不大,称为三号数以便于讨论。
那么根据以上的分类,可以对应地可以得出对可行方案的限制:
- 不存在 \(a_i \geq x\);
- 没有两个相邻的一号数;
- 没有一号数与二号数相邻;
- 二号数之间可以相邻。
思路
那把题意这么一理清楚就变成了一个组合问题了。首先将所有的数分类,就类似于将小球染成不同颜色。然后根据规则排列小球。
对于第一条限制输入的时候判一下无解就可以了。
我的第一想法是,将一号数视为挡板,然后每两个挡板之间都必须要有三号数。但是这样对于二号数的约束就比较难以实现,于是考虑将二号数作为挡板,然后对于每一个一号数都要用两个三号数包起来。想了想比较复杂,遂将三号数作为挡板,这样问题就简单了。
将三号数作为挡板,两个挡板之间的空中可以放一个一号数或若干个二号数,也可以放滚木。
然后就变成一个纯粹的排列组合问题了。
假设记录的三种数字个数分别为 \(cnt_1,cnt_2,cnt_3\)。
首先考虑无解的情况。\(cnt_3\) 个三号数,算上两端,可以组成 \(cnt_3 + 1\) 个空。这个时候,如果没有二号数,则 \(cnt_1 > cnt_3 + 1\) 时无解;如果有二号数,则二号数至少占据一个空,\(cnt_1 + 1 > cnt_3 + 1\) 时无解。这时答案为 \(0\)。显然除此之外都是有解的情况。
其次考虑任意排列均可以的情况。显然,不存在一号数时任意排列均可;只存在一个一号数时,若不存在二号数也是任意排列均可。这时答案为 \(n !\)。
最后则是一般情况。我将这整个过程拆分为以下几步:
-
放三号数:挡板的放置方案有 \(cnt_3 !\) 种。
-
放一号数:将一号数任意填入 \(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!}\)。
-
放二号数:剩下的有 \(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)\)。
代码
有几个代码实现上的细节问题,梳理一下:
- 如果你和上面一样的分数:\(x\) 就是 \(2\) 的整数次幂,那么 \(a_i = x\) 时无解,\(\frac{x}{2} + \frac{x}{2}\) 不会超过 \(x\),所以此时不存在一号数;
- 如果你错最后一个测试点:如果 \(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;
}
完结撒花喵!
本文来自博客园,作者:Circle_Table,转载请注明原文链接:https://www.cnblogs.com/Circle-Table/articles/19563940

浙公网安备 33010602011771号