题意是给你两个长度为$n$的排列,他们分别是$n$的第$a$个和第$b$个全排列。输出$n$的第$\left(a+b \right)\textrm{mod} \, n!$个全排列。

一种很容易的想法是直接把$a$和$b$求出来,然后计算$\left(a+b \right)\textrm{mod} \, n!$的值并直接求对应的排列,但是由于$n$的范围$\left(n\leq200000\right)$直接求值显然不可行。

因此,考虑全排列的康托展开(Cantor expansion) 任意一种排列在全排列中对应的序号为$$\sum_{i=1}^{n}{a}_{i}\times i!$$

于是,将输入的两个排列分别写成这种形式,然后遍历$n$相加,由于结果需要对$n!$取模,因此从最低位开始逐项将$a_i$加到$a_i+1$上去,最后将最高位的$a_n$模掉$n$即可。

之后,只要拟用康托展开即可求出对应的排列。

在实现过程中,由于需要维护"当前还没有使用过的第k大的数",因此可以用树状数组BIT维护。恢复排列时用树状数组+二分即可。

复杂度$\mathcal{O}({n\log}^{2}n )$

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define GETNUM(num) scanf("%d",&num)
#define IT_PT(BEG,END,TYPE,REG) copy(BEG,END,ostream_iterator<TYPE>(cout,REG))
#define CLR(ARR,NUM) memset(ARR,NUM,sizeof(ARR))
#define faster_io() ios_base::sync_with_stdio(false)
using namespace std;
const int MAXN = 200010;
int la[MAXN], lb[MAXN], a[MAXN], b[MAXN], f[MAXN], s[MAXN];
typedef int bit_type;
const int bit_maxn = MAXN;
int n;
int ff[MAXN];
bit_type tree[bit_maxn]; int lowbit(int x)
{
return x & (-x);
} void add(int x, int d)
{
x++;
while(x <= n) {
tree[x] += d;
x += lowbit(x);
}
} bit_type sum(int x)
{
x++;
bit_type ans = 0;
while(x) {
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
int main()
{
cin >> n;
for(int i = 0; i < n; i++)
GETNUM(la[i]);
for(int i = 0; i < n; i++)
GETNUM(lb[i]);
CLR(tree, 0);
for(int i = 0; i < n; i++) {
add(i, 1);
}
for(int i = 0; i < n; i++) {
a[i] = sum(la[i] - 1);
add(la[i], -1);
}
CLR(tree, 0);
for(int i = 0; i < n; i++) {
add(i, 1);
}
for(int i = 0; i < n; i++) {
b[i] = sum(lb[i] - 1);
add(lb[i], -1);
s[i] = a[i] + b[i];
} CLR(tree, 0);
for(int i = 0; i < n; i++) {
add(i, 1);
ff[i] = 1;
}
for(int i = n - 1; i > 0; i--) {
s[i - 1] += s[i] / (n - i);
s[i] %= (n - i);
}
s[0] %= n;
int rr = n - 1;
for(int i = 0; i < n; i++) {
int r = rr;
int l = 0;
while(l < r) {
int mid = l + (r - l + 1) / 2;
int t = sum(mid - 1);
if(t <= s[i]) l = mid;
else r = mid - 1;
}
add(l, -1);
ff[l] = 0;
if(!i) {
printf("%d", l);
} else {
printf(" %d", l);
}
while(!ff[rr]) {
rr--;
}
}
return 0;
}

[Codeforces 501D] - Misha and Permutations Summation的更多相关文章

  1. Misha and Permutations Summation

    A - Misha and Permutations Summation 首先这个 mod n! 因为数量级上的差别最多只会对康拓展开的第一项起作用所以这个题并不需要把 ord (p) 和 ord ( ...

  2. 【codeforces 501D】Misha and Permutations Summation

    [题目链接]:http://codeforces.com/problemset/problem/501/D [题意] 给你两个排列; 求出它们的字典序num1和num2; 然后让你求出第(num1+n ...

  3. Codeforces Round #285 (Div.1 B & Div.2 D) Misha and Permutations Summation --二分+树状数组

    题意:给出两个排列,求出每个排列在全排列的排行,相加,模上n!(全排列个数)得出一个数k,求出排行为k的排列. 解法:首先要得出定位方法,即知道某个排列是第几个排列.比如 (0, 1, 2), (0, ...

  4. Codeforces Round #285 (Div. 1) B - Misha and Permutations Summation 康拓展开+平衡树

    思路:很裸的康拓展开.. 我的平衡树居然跑的比树状数组+二分还慢.. #include<bits/stdc++.h> #define LL long long #define fi fir ...

  5. CF501D Misha and Permutations Summation(康托展开)

    将一个排列映射到一个数的方法就叫做康托展开.它的具体做法是这样的,对于一个给定的排列{ai}(i=1,2,3...n),对于每个ai求有多少个aj,使得j>i且ai>aj,简单来说就是求a ...

  6. CodeForces 501B Misha and Changing Handles(STL map)

    Misha hacked the Codeforces site. Then he decided to let all the users change their handles. A user ...

  7. codeforces 501C. Misha and Forest 解题报告

    题目链接:http://codeforces.com/problemset/problem/501/C 题目意思:有 n 个点,编号为 0 - n-1.给出 n 个点的度数(即有多少个点跟它有边相连) ...

  8. Codeforces Round #337 Alphabet Permutations

    E. Alphabet Permutations time limit per test:  1 second memory limit per test:  512 megabytes input: ...

  9. codeforces 341C Iahub and Permutations(组合数dp)

    C. Iahub and Permutations time limit per test 1 second memory limit per test 256 megabytes input sta ...

随机推荐

  1. java枚举小结

    如何定义一个枚举类? //定义了4个等级 enum Level{ A,B,C,D } 枚举类的实质: class Level{ public static final Level A = new Le ...

  2. nginx+uwsgi+django

    上一涨讲解了如何使用nginx+uwsgi部署wsgi application 其实django配置方式和 application都一样,因为如果我们对application进行扩展就是一个WSGI ...

  3. xamarin SimpleAdapter绑定出错问题

    问题:今天在实验xamarin中SimpleAdapter绑定到ListView时,出现闪退的现象, 见图: 解决方法: SimpleAdapter中的构造函数public SimpleAdapter ...

  4. WPF会重写Windows GUI的历史吗?

    原文地址:http://tech.it168.com/zx/2007-09-15/200709141320653.shtml 你可能对微软的.NET框架3.0版本的最近的一次更新感到有点奇怪.主版本指 ...

  5. BZOJ 1024 生日快乐

    Description windy的生日到了,为了庆祝生日,他的朋友们帮他买了一个边长分别为 X 和 Y 的矩形蛋糕.现在包括windy,一共有 N 个人来分这块大蛋糕,要求每个人必须获得相同面积的蛋 ...

  6. X-Plosives

    uvaLive 3644:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&pa ...

  7. Qt学习 之 Socket通信(世界上最简单的例子了)

    最近写大作业用到Qt的Socket部分,网上关于这部分的资料都太过复杂,现在总结一下一些简单的应用.有机会可以给大家讲讲用Socket传送文件的代码. 这里主要讲解如何实现TCP和UDP的简单通信. ...

  8. 一句话改变TWinControl控件的left坐标的前世今生(入口函数是SetBounds,然后调用SetWindowPos起作用,并发消息更新Delphi的left属性值)

    Delphi的重要属性,主要是Enable,  Visible, Color, left等等.这里分析left,因为TWinControl里有些覆盖函数的原因,虽然起点都是TControl.SetLe ...

  9. 【HDOJ】1483 Automatic Correction of Misspellings

    水模拟题. /* 1483 */ #include <cstdio> #include <cstring> #include <cstdlib> #define M ...

  10. c++实现委托

    #include "stdafx.h" #include <iostream> #include <string> using namespace std; ...