题解:CF2040C Ordered Permutations
CF2040C 题解
题面
题面
定义一个序列 a 的权值为 ∑1⩽l⩽r⩽nmin(al,al+1,⋯,ar),给定 n 和 k,问长度为 n 的排列中权值最大的所有排列中字典序排第 k 大的排列,若没有则输出 -1
。
思路
首先先分析一个数列的权值的求法,单看题目中给的式子有点难想什么时候是最大的,考虑分析排列中每个数字会造成的贡献,设 i 的位置为 pi,i 左边第一个比 i 小的数的位置为 li,右边第一个比 i 小的数的位置为 ri,则 i 对序列的贡献为 i×(pi−li)×(ri−pi)。
接下来考虑什么时候序列的权值会取到最大值,首先肯定要确保大的数尽可能的被多选,而对于一个序列的权值最大就为 n∑i=1i×(n−i+1)。
接下来考虑如何才会构成这种权值最大的情况,其实也很简单,首先我们知道对于一个数 i 他的贡献为 i×(n−i+1),所以 (pi−li)×(ri−pi)=n−i+1。
容易想到(或者说暴力打个表),权值最大的排列的构造方式是从小到大放入每个数,每次放入一个数只能放在当前序列的最左边或者最右边,这样就可以满足每个数字的贡献。
所以权值最大的排列数为 2n−1,所以若 k>2n−1,则无解。
最后考虑字典序第 k 大的排列,因为数字是从小到达放的,每个数字都有放左边或放右边两种选择,所以放入 i 后还有 2n−i−1 种放法,即只要用 k 与 2n−i+1 比较就可以决定当前 i 是放最左端还是最右端,具体的有如下。
- 若 k<=2n−i+1,则 i 放左端。
- 若 k>2n−i+1,则 i 放右端,并将 k 减去 2n−i+1。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int MN=2e5+5;
ll n,k,ans[MN];
void write(ll n){if(n<0){putchar('-');write(-n);return;}if(n>9)write(n/10);putchar(n%10+'0');}
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
ll ksm(ll a, ll b){ll res=1;while(b){if(b&1)res=res*a;a=a*a;b>>=1;}return res;}
void solve(){
for(int i=1; i<=n; i++) ans[i]=0;
n=read();k=read();
if(n<=40&&k>ksm(2,n-1)){write(-1);putchar('\n');return;}
ll l=1,r=n,op=0;
for(int i=1,j=n-2; i<n; i++,j--){
if(j>40){ans[l++]=i;continue;}//作者怕爆 long long
if(k>ksm(2,j)){
ans[r--]=i;
k-=ksm(2,j);
}
else{ans[l++]=i;}
}
ans[l]=n;
for(int i=1; i<=n; i++) write(ans[i]),putchar(' ');putchar('\n');
}
int main(){
ll T=read();while(T--)solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现