题解 P8757 [蓝桥杯 2021 省 A2] 完美序列
题解 P8757 [蓝桥杯 2021 省 A2] 完美序列
题意#
如果一个序列是单调递减的,而且除了第一个数以外的任何一个数都是上一个数的因数,则称这个序列为一个完美序列。
一个序列中的一个子序列如果是完美序列,则称为该序列的一个完美子序列。一个序列的最长完美子序列长度,称为该序列的完美长度。
给定正整数
给定正整数
思路#
显然地,
考虑如何证明。考虑每次除的数,构成了一个长为
- 如果替换掉一个数。显然只可能替换成质数。
如果 ,那么可以将序列中的一位替换成 ,此时最大完美长度不变。
如果替换成 ,显然 。这样最大位就比 大了,肯定是不行的。
大于 的数同理。
因此,只可能把序列中的某位替换成 。 - 如果替换掉多个数。还是值可能替换成质数。
如果替换两个 ,显然 。这样最大位就比 大了,肯定不行。
显然,如果替换成更大或者更多的数,也一定不行。
在这个过程中,我们发现,这个序列中,只可能有
考虑计算每个数可以贡献多少次。假设现在是从小到大的第
首先,要在
现在排列的位置确定了,考虑有多少种排列会让
- 如果
的因子中没有 ,那么如果 ,则有 个位置可以被替换成 。
再加上全部是 的一种,总共就是 种。 - 如果
的因子中有 ,那么有 个位置可以放 。
总共就是 种。
枚举即可,复杂度
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int ans=0;bool op=0;char ch=getchar();
while(ch<'0'||'9'<ch){if(ch=='-')op=1;ch=getchar();}
while('0'<=ch&&ch<='9'){ans=(ans<<1)+(ans<<3)+(ch^48);ch=getchar();}
if(op)return -ans;
return ans;
}
const int maxn=1e6+10;
const int mod=1e9+7;
int n;
int fact[maxn];
int ifac[maxn];
int len;
int base;
int ans=0;
int inv(int a){
int ans=1;
for(int i=mod-2;i;i>>=1){
if(i&1)ans=ans*a%mod;
a=a*a%mod;
}
return ans;
}
int C(int a,int b){
if(b>a||a<0||b<0)return 0;
return fact[a]*ifac[b]%mod*ifac[a-b]%mod;
}
void real_main(){
n=read();
if(n==1){
puts("1");
return;
}
len=__lg(n)+1;
base=1;
ans=0;
for(int i=1;i<=len;i++){//只有2的
ans=(ans+C(n,len)*base%mod*fact[n-len]%mod)%mod;
base<<=1;
}
if((1<<(len-2))*3>n){
cout<<ans<<'\n';
return;
}
base=1;
for(int i=1;i<=len;i++){//不含3的
ans+=C(n,len)*base*(len-i)%mod*fact[n-len]%mod;
ans%=mod;
base<<=1;
}
base=3;
for(int i=2;i<=len;i++){//含3的
ans+=C(n,len)*base*(i-1)%mod*fact[n-len]%mod;
ans%=mod;
base<<=1;
}
cout<<ans<<'\n';
}
signed main(){
fact[0]=1;
for(int i=1;i<maxn;i++)fact[i]=fact[i-1]*i%mod;
for(int i=0;i<maxn;i++)ifac[i]=inv(fact[i]);
int T=read();
while(T--)real_main();
return 0;
}
优化#
这样已经可以过了,但是还可以优化一下。
容易发现,每个式子都含有C(n,len)*fact[n-len]
。
定义
或者,容易发现,
观察全
观察不含
观察含
现在,我们把每一个计算都优化成了递推,可以预处理。单次询问
复杂度
@TQI_II为机房公用账号,上面也是我交的,会在下面发个评论证明一下。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int ans=0;bool op=0;char ch=getchar();
while(ch<'0'||'9'<ch){if(ch=='-')op=1;ch=getchar();}
while('0'<=ch&&ch<='9'){ans=(ans<<1)+(ans<<3)+(ch^48);ch=getchar();}
if(op)return -ans;
return ans;
}
const int maxn=1e6+10;
const int mod=1e9+7;
int n;
int fact[maxn];
int ifac[maxn];
int len;
int base;
int ans=0;
int pre2[maxn];
int pre23[maxn];
int pre33[maxn];
int inv(int a){
int ans=1;
for(int i=mod-2;i;i>>=1){
if(i&1)ans=ans*a%mod;
a=a*a%mod;
}
return ans;
}
signed main(){
fact[0]=1;
for(int i=1;i<maxn;i++)fact[i]=fact[i-1]*i%mod;
ifac[20]=inv(fact[20]);
for (int i=19;i>=0;i--)ifac[i]=(ifac[i+1]*(i+1))%mod;
base=1;
for(int i=1;(1<<(i-1))<maxn;i++){
pre2[i]=(pre2[i-1]+base)%mod;
pre23[i]=(pre23[i-1]+base-1)%mod;
base<<=1;
}
base=3;
for(int i=2;(1<<(i-1))<maxn;i++){
pre33[i]=(pre33[i-1]+base*(i-1))%mod;
base<<=1;
}
int T=read();
while(T--){
n=read();
if(n==1){
puts("1");
continue;;
}
len=__lg(n)+1,ans=0;
int tot=fact[n]*ifac[len]%mod;
ans=(ans+pre2[len]*tot%mod)%mod;
if((1<<(len-2))*3>n)cout<<ans<<'\n';
else cout<<(ans+tot*(pre23[len]+pre33[len])%mod)%mod<<'\n';
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现