1 条题解
-
0
自动搬运
来自洛谷,原作者为

gcwixsxr
lxyddd搬运于
2025-08-24 21:27:29,当前版本为作者最后更新于2020-08-19 22:58:29,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
本蒟蒻做这道题时,还只有一个题解,于是本蒟蒻也想分享一下自己的方法。
首先找一下规律:(以下01串均为二进制表示)
当 为 时, 满足要求的数据为 。
当 为 时, 满足要求的数据为 。
当 为 时, 满足要求的数据为 。
可以发现,每 个满足要求的数据中,从第 个到第 个数的数值分别等于第 个到第 个数的数值加上 ,即 ,其中 ,而第 个数的数值等于第 个数加上 ,即 。
举个栗子,当 为 时,如下图,后 个分别等于前 个的数值加上 ,第 个的值等于第 个加 ;

还不信?再看看 为 的时候:
当 为 时, 满足要求的数据为: ,
同样是满足这个规律的,对吧,按照这个规律,可以算出 到 中的任何一个数。
按照此规律,能算出任何排名的数的函数如下。
long long work(long long k){//求出排名第k的数的数值 if(k==1)return 0LL;//如果是第一项,返回0 if(k==2)return 1LL;//如果是第二项,返回1 int t=0;long long kl=1; while(kl<k){ kl<<=1; t++; } t--;kl>>=1;//算出小于k的最大2的次幂 if(k-kl==1)return kl+work(k-1); else return kl+work(k-kl-1);//这两句对应上方规律中的两个递推公式 }那么,算出排名第 的数有什么用呢?
这个时候要用到分治的思想了:
我们先看张图

假设我们要求前 个中的最大值,即求 ,由于后 个一定大于前 个,又因为 ,并且 ,所以,问题就被转化成了求 ,即求前 个和第 个的的最大值,因此上述的问题被转化到了下图中:

求,第 个一定大于前 个,因此问题又被转化,即求,(其实这个时候可以直接调用 函数,但也可以继续递推下去),继续转化,,问题变为求.

来到最后一步,,即求,这时就可以输出答案辣!
像这样大事化小,小事化了,不就是分治吗?
求解函数;
long long solve(long long k){ if(k==1)return 0LL;//返回1 if(k==2)return 1LL;//返回0 int t=0;long long kl=1; while(kl<k){ kl<<=1; t++; } t--;kl>>=1; if(k-kl==1)return kl+work(k-1);//如果是2次幂+1的形式,最大的就是自身 else return (long long)kl+max(solve(k-kl-1),work(kl));//特判一下中间特殊的一项 }最后是代码:
#include<bits/stdc++.h> using namespace std; int n; long long k; long long work(long long k){ if(k==1)return 0LL; if(k==2)return 1LL; int t=0;long long kl=1; while(kl<k){ kl<<=1; t++; } t--;kl>>=1; if(k-kl==1)return kl+work(k-1); else return kl+work(k-kl-1); } long long solve(long long k){ if(k==1)return 0LL; if(k==2)return 1LL; int t=0;long long kl=1; while(kl<k){ kl<<=1; t++; } t--;kl>>=1; if(k-kl==1)return kl+work(k-1); else return (long long)kl+max(solve(k-kl-1),work(kl)); } void write(long long x, int t){ if(t-1) write(x>>1,t-1); putchar(x&1?'1':'0'); } signed main(){ scanf("%d%lld",&n,&k); write(solve(k),n); return 0; }
- 1
信息
- ID
- 639
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者