CF501D题解

CF501D

同步于本博客

如果你还不会康托展开,或者想要更好的用户体验,欢迎莅临本蒟蒻的博客,如果你还不会平板电视,也可以参考一下蒟蒻的博客

思路

本题也是一道需要对康托展开的式子比较熟悉才能做出来的题目,不难看出这个 \(n!\) 很恶心,同时 \(n\le 2\times10^5\) 的数据范围也不允许我们直接计算结果。因此,对于本题我们需要做一下转换。

借用上一题题目给出的式子:

\[\sum_{i=1}^KS_i\times(K-i)! \]

可以发现,对于本题来说仍然有效。

我们下标从 \(1\) 开始,假设第一个排列为 \(a\),第二个排列为 \(b\),我们要求的排列为 \(c\),我们就可以得到:

\[\sum_{i=1}^nc_i\times(n-i)!=\sum_{i=1}^na_i\times(n-i)!+b_i\times(n-i)! =\sum_{i=1}^n(a_i+b_i)\times(n-i)! \]

因此 \(c_i=a_i+b_i\),我们就可以通过简单的加和得到 \(c_i\)

接下来我们再来处理 \(n!\),显然我们会因为庞大的数据范围而无法直接计算。但是我们发现对于 \(n\) 个数的排列,最多也只有 \(n!\)。因此如果一种方案超出 \(n!\),说明它一定是不合法的,某些数超出了我们设定的范围。因此我们可以通过进位的方式来解决,这种方式及其巧妙,使得我们可以不用算出 \(n!\) 的大小就可以解决问题(蒟蒻当时就是因为没想到应该怎么转化才没做出来这题)。

最后就是简单的逆康托展开求出排列了,特别的,本题的排列是从 \(0\) 开始的,因此处理的时候边界问题可能会有些繁琐。

代码实现

还是老套路,只用码量小的平板电视和树状数组,注释应该比较清楚,这里就不再解释啦。

#include<iostream>
using namespace std;
//———————————————————————————————————————平板电视红黑树的命名start
#include<ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>rbt;
//———————————————————————————————————————平板电视红黑树的命名end
typedef long long ll;
const int N=2e5+10;
int n,a[N],b[N];//a存储输入的两个数组,b存储新数组
//———————————————————————————————————————树状数组start
ll t[N];
#define lowbit(x) ((x)&-(x))
void add(int a,int x)
{
    for(int i=a;i<=n;i+=lowbit(i))t[i]+=x;
}
ll query(int a)
{
    ll res=0;
    for(int i=a;i;i-=lowbit(i))res+=t[i];
    return res;
}
//———————————————————————————————————————树状数组end
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=n;i;i--)
    {
        add(a[i]+1,1);//注意将下标+1,否则会引发数组越界
        b[i]=query(a[i]);//存储查询结果
    }
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)add(i,-1);//恢复树状数组
    for(int i=n;i;i--)
    {
        add(a[i]+1,1);
        b[i]+=query(a[i]);
    }
    for(int i=n;i;i--)
	{
		b[i-1]+=b[i]/(n-i+1);//借鉴了DengDuck大佬的代码思路
		b[i]%=(n-i+1);//处理进位问题
	}
    for(int i=0;i<n;i++)rbt.insert(i);//先将所有元素加入红黑树中
    for(int i=1;i<=n;i++)
    {
        auto it=rbt.find_by_order(b[i]);//找第b[i]大的元素
        cout<<*it<<' ';
        rbt.erase(it);
    }
    return 0;
}
posted @ 2023-07-23 22:24  week_end  阅读(2)  评论(0编辑  收藏  举报