lb摸鱼系列1

题目描述

传送门

思路

就是一道火星人的升级版。

其实核心思想就是康托,但5e5的全排列受不鸟,考虑优化康托。

其实康托个数就是排列个数,根据 n ! = n ∗ ( n − 1 ) ∗ ( n − 2 ) ∗ ⋯ ∗ 1 n!=n*(n-1)*(n-2)*\cdots*1 n=n(n1)(n2)1

因此第一个位置可以有n种选择,由于第一个数被选了,第二个数只有(n-1)种选择,以此类推,恰好为 n ! n! n,正好对应进制数,第 i i i位的进制就是 n − i + 1 n-i+1 ni+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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值