版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82812120
大意
求 的全排列的第 个
思路
裸的逆康拓展可以直接拿60,套上高精度可拿70(优化可到80,甚至90)
但是,我们也可以通过一些公式来优化
具体证明我也不懂,总之就是把逆康拓展先打出来,然后按公式改即可
具体证明:
还有下面这个玩意儿。。。
证明:
则
发现每次的除数都是之前的约数。
所以对于第i次除法的商,可以直接用
得到。
根据第二个结论,
观察这个式子,再结合推论1,我们发现可以从大到小枚举
,每次对
除以i,得到的余数就是原第
次除法的商。
因此求商的复杂度降为
,
是
在十进制下有多少位。
由于只需要单精度除法,可以用long long压8位来做,并且随着被除数的不断减小,每次除法的复杂度降低。
所以复杂度大概是
。
这一部分的复杂度和
无关
最后一个问题,怎么快速找当前没有被选的数中的第a大的数?
直接暴力是
的。
套用数据结构(zkw)线段树,树状数组,splay或者分块思想都可以解决这个问
题,但是这显然超纲了
观察数据发现, ,什么意思?
也就是最后一组数据的前 都为 这样我们就只需要在后面 个数中查找没有被选中的第 大的数
这样的复杂度就是 的,可以卡过本题。。。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ymw 100000000//高精度压8位
using namespace std;
int l,n,len,t,k;char s[100001];
long long a[4001],kth[100001];
bool bz[100001];
signed main()
{
scanf("%d %s",&n,s);
len=strlen(s);
l=4000;t=1;
for(register int i=len-1;i>=0;i--)
{
a[l]+=(s[i]-48)*t;
t=(t<<3)+(t<<1);
if(t==ymw) t=1,l--;//每八位换下一个
}
int j=4000;
for(;!a[j];a[j]=ymw-1,j--);//把所有的0都当成为99999999
--a[j];//因为逆康拓展是求前面有多少个比它大的,而我们是要求第k个,所以要减一
for(register int i=1;i<=n;i++)
{
for(register int j=l;j<4000;j++)
{
long long x=a[j]/i;
a[j+1]+=ymw*(a[j]-x*i);a[j]=x;//高精度除法
}
kth[n-i+1]=a[4000]%i+1;a[4000]/=i;//最后一位特判
while(!a[l]) l++;//没了就往后推,节省循环
}
k=max(0,n-6100);//前面的数直接输出
for(register int i=1;i<=k;i++) printf("%d ",i);//输出
for(register int i=k+1;i<=n;i++)
{
int j=k+1,cnt=0;
for(;cnt<kth[i];j++) if(!bz[j])++cnt;//找到第cnt个
bz[--j]=1;printf("%d ",j);//前面cnt个都找到了,输出
}
}