版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/82813059
正题
题目大意
求n个数的全排列的第k个。
解题思路
首先康拓逆展开
求每个时候第
大的数
然后因为
so我们可以直接用余数
这是
时是序列,我们可以发现我们需要求第
个。这时候我们可以用二分加树状数组来
的求答案。
之后我们又会发现
这个直接计算时间复杂度是
(w是高进度位数)。
这时我们引入:
证明:
这时候我们可以枚举
,然后每次
,这时候的余数就是上面式子
次的余数。
code
#include<iostream>
#include<cstring>
#define lobit(x) x&-x
#define N 100010
#define ll long long
using namespace std;
ll a[N*2],n,t[N],l,mo[N];
char k[N*2];
void change(ll x,ll num)//改变
{
while(x<=n)
{
t[x]+=num;
x+=lobit(x);
}
}
ll ask(ll x)//询问
{
ll sum=0;
while(x)
{
sum+=t[x];
x-=lobit(x);
}
return sum;
}
void read()//输入——高精度
{
scanf("%s",k);
l=strlen(k)-1;
ll L=0;
for(ll i=l;i>=0;i--)
L+=((l-i)%13)==0,mo[i]=L-1;//压行之后的位置
for(ll i=0;i<=l;i++)
a[mo[i]]=a[mo[i]]*10+k[i]-48;//求值
a[0]--;
for(ll i=0;i<=l;i++)
if(a[i]<0) a[i+1]--,a[i]+=(1e13);//要先-1
l=L-1;
}
ll div(ll x)//高精除
{
ll g=0;
for (ll i=l;i>=0;i--)
{
ll s;
s=g*(1e13)+a[i];
a[i]=s/x;
g=s%x;
}
while(!a[l]) l--;
return g;
}
int main()
{
scanf("%lld",&n);
read();
for(ll i=1;i<=n;i++)
mo[n-i+1]=div(i);//计算余数
for(ll i=1;i<=n;i++) {
ll ans=0;
for(ll l=1,r=n;l<=r;) {
ll m=(l+r)/2;
ll s=0,x=m;
s=x-ask(x);
if(s<=mo[i]) l=m+1;
else ans=m,r=m-1;
}//二分位置
ll x=ans;
change(x,1);//去掉这个数
printf("%lld ",ans);
}
return 0;
}