题目描述
思路
就是一道火星人的升级版。
其实核心思想就是康托,但5e5的全排列受不鸟,考虑优化康托。
其实康托个数就是排列个数,根据 n ! = n ∗ ( n − 1 ) ∗ ( n − 2 ) ∗ ⋯ ∗ 1 n!=n*(n-1)*(n-2)*\cdots*1 n!=n∗(n−1)∗(n−2)∗⋯∗1
因此第一个位置可以有n种选择,由于第一个数被选了,第二个数只有(n-1)种选择,以此类推,恰好为 n ! n! n!,正好对应进制数,第 i i i位的进制就是 n − i + 1 n-i+1 n−i+1。
再考虑 + m +m +m,其实也就是字典序 + m +m +m,可以将 u n k o w n unkown unkown进制的最低位直接加上一个m,让后模拟加法进位即可,进制数每一个位上的数 a i + 1 a_i+1 ai+1,也就对应着康托展开第 ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ ⋯ ∗ ( a n + 1 ) (a_1+1)*(a_2+1)*\cdots*(a_n+1) (a1+1)∗(a2+1)∗⋯∗(an+1)
之后数据结构乱搞。
AC code
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define gc getchar()
#define ll long long
using namespace std;
const int N=5e5+10;
template<class o>
inline void qr(o &x)
{
x=0;char c=gc;
while(c<'0'||c>'9')c=gc;
while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
}
void qw(ll x)
{
if(x/10)qw(x/10);
putchar(x%10+48);
}
ll a[N];int c[N],n; ll m;
void add(int x){for(;x<=n;x+=x&-x)++c[x];}
int ask(int x){int ans=0;for(;x;x-=x&-x)ans+=c[x];return ans;}
struct segtree{int l,r,sum;}t[N*4];int cnt;
void build(int &x,int l,int r)
{
x=++cnt;
if(l==r){t[x].sum=1;return ;}
int mid=(l+r)>>1;
build(t[x].l,l,mid);
build(t[x].r,mid+1,r);
t[x].sum=t[t[x].l].sum+t[t[x].r].sum;
}
void change(int l,int r,int x,int pos)
{
--t[x].sum;
if(l==r)return ;
int mid=(l+r)>>1;
if(pos<=mid)change(l,mid,t[x].l,pos);
else change(mid+1,r,t[x].r,pos);
}
int query(int l,int r,int x,int k)
{
if(l==r)return l;
int mid=(l+r)>>1;
if(k<=t[t[x].l].sum)return query(l,mid,t[x].l,k);
else return query(mid+1,r,t[x].r,k-t[t[x].l].sum);
}
int main()
{
//freopen("a4.in","r",stdin);
// freopen("a4.out","w",stdout);
qr(n),qr(m);
for(int i=1;i<=n;i++)qr(a[i]);
for(int i=n,x;i>=1;i--)
{
x=a[i];a[i]=ask(a[i]-1);
add(x);
}
a[n]+=m;
for(int i=n;i>=2;i--)
{
a[i-1]+=a[i]/(n-i+1);
a[i]%=n-i+1;
}
int root=0;
build(root,1,n);
for(int i=1;i<=n;i++)
{
++a[i];
a[i]=query(1,n,1,a[i]);
qw(a[i]),putchar(' ');
change(1,n,1,a[i]);
}
puts("");
return 0;
}