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;
} 
posted @ 2025-05-19 18:59  Zom_j  阅读(32)  评论(0)    收藏  举报