P8757 [蓝桥杯 2021 省 A2] 完美序列(题解)
要解决这道题,我们首先就要求出一个排列的完美子序列的最大长度是多少。
贪心地考虑,若要使因数更多,相邻数的差距就要更小,那么排列为 \(2^{k},2^{k-1},2^{k-2} ... 2^{0}\) 时最优,其中 \(2^{k}<=n\)这样我们就锚定了最大的长度。
接着就考虑在这个最大长度下,有哪些排列符合要求,我们可以进行底数的更换,首先考虑修改一个数的情况:
将 \(2^{n}\) 改为 \(2^{n-1} \times a (a>=3)\)。
当 \(a=3\) 时:
对于只替换一个 3 的情况,不难发现只有 \(2^{k-1} \times 3<=n\) 时是合法的。
若在序列中将 2 个及以上的 2 替换为 3,我们可以发现是明显不优的,举一个例子,对于 \(n=8\) 时就有 \((8,4,2,1)\) 而 \(n=9\) 时才有 \((9,3,1)\)。
当 \(a=4\) 时:我们将 \(4^{x}\) 拆为\({2^{x},2^{2x}}\) 会更优。
对于 \(a>4\) 的情况,容易证明不会比 \(a=4\) 的情况更优,所以劣于 \(a=3\) 的情况。
综上所述最多只有两类完美子序列:有 3 为因子的序列和无 3 为因子的序列。
接下来考虑如何计算贡献,对于两类合法序列,它的排列顺序是固定的,个数也是固定的,k 个数的排列数为 \(\displaystyle \binom{n}{k}\),而剩下的 \(n-k\) 个数顺序是不固定的,所以方案数为 \(\displaystyle \binom{n}{k}\times(n-k)!\),不难发现对于每种合法序列的方案数都是这么多,由于一种排列可以对应多个子序列,所以方案数是独立的不需要容斥,那么我们只要计算每种情况的价值就行了,那么这样代码就可以写了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int P=1e9+7;
const int N=1e6+10;
int fac[N];
int ifac[N];
int T;
int qp(int base,int k){
int res=1;
while(k){
if(k&1){
res*=base;
res%=P;
}
base*=base;
base%=P;
k>>=1;
}
return res;
}
void init(){
int M=1e6;
fac[0]=fac[1]=ifac[0]=ifac[1]=1;
for(int i=2;i<=M;i++){
fac[i]=fac[i-1]*i%P;
}
ifac[M]=qp(fac[M],P-2);
for(int i=M-1;i>1;i--){
ifac[i]=ifac[i+1]*(i+1)%P;
}
}
int C(int a,int b){
return fac[a]*ifac[a-b]%P*ifac[b]%P;
}
int log_(int x){
int res=-1;
while(x){
res++;
x>>=1;
}
return res;
}
signed main(){
cin>>T;
init();
while(T--){
int n;
cin>>n;
if(n==1){
cout<<1<<endl;
continue;
}
int ans=0;
int k=log_(n)+1;
int sum=0;
for(int i=0;i<k;i++){
sum+=qp(2,i);
}
ans=sum*C(n,k)%P*fac[n-k]%P;
if(qp(2,k-2)*3<=n){
for(int i=k-1;i>=1;i--){
sum=sum-qp(2,i)+qp(2,i-1)*3;
ans+=(sum*C(n,k)%P*fac[n-k]%P);
ans%=P;
}
}
cout<<ans<<endl;
}
return 0;
}

浙公网安备 33010602011771号