正题
题目大意
求n个数的全排列的第k个。
解题思路
首先康拓逆展开
∑ii<=nxi(n−i)!\sum^{i<=n}_i x_i(n-i)!∑ii<=nxi(n−i)!
求每个时候第xxx大的数
然后因为n(n−1)!=n!n(n-1)!=n!n(n−1)!=n!
so我们可以直接用余数
这是n=3n=3n=3时是序列,我们可以发现我们需要求第xix_ixi个。这时候我们可以用二分加树状数组来log2log^2log2的求答案。
之后我们又会发现
∑ii<=nxi(n−i)!\sum^{i<=n}_i x_i(n-i)!∑ii<=nxi(n−i)!
这个直接计算时间复杂度是n2log2nwn^2\ log^2n\ wn2 log2n w(w是高进度位数)。
这时我们引入:
x%yz/z=x/z%yx\%yz/z=x/z\%yx%yz/z=x/z%y
证明:
这时候我们可以枚举1∼n1\sim n1∼n,然后每次kmodik\ mod\ ik mod i,这时候的余数就是上面式子n−i+1n-i+1n−i+1次的余数。
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);//要先-1l=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;
}