传送门:点击打开链接
Let's define the sum of two permutations p andq of numbers0, 1, ..., (n - 1) as permutation, wherePerm(x) is the x-th lexicographically permutation of numbers0, 1, ..., (n - 1) (counting from zero), andOrd(p) is the number of permutationp in the lexicographical order.
For example, Perm(0) = (0, 1, ..., n - 2, n - 1),Perm(n! - 1) = (n - 1, n - 2, ..., 1, 0)
Misha has two permutations, p and q. Your task is to find their sum.
Permutation a = (a0, a1, ..., an - 1) is called to be lexicographically smaller than permutation b = (b0, b1, ..., bn - 1), if for some k following conditions hold: a0 = b0, a1 = b1, ..., ak - 1 = bk - 1, ak < bk.
The first line contains an integer n (1 ≤ n ≤ 200 000).
The second line contains n distinct integers from0 ton - 1, separated by a space, forming permutationp.
The third line contains n distinct integers from0 ton - 1, separated by spaces, forming permutationq.
Print n distinct integers from 0 to n - 1, forming the sum of the given permutations. Separate the numbers by spaces.
2 0 1 0 1
0 1
2 0 1 1 0
1 0
3 1 2 0 2 1 0
1 0 2
Permutations of numbers from 0 to 1 in the lexicographical order: (0, 1), (1, 0).
In the first sample Ord(p) = 0 andOrd(q) = 0, so the answer is.
In the second sample Ord(p) = 0 andOrd(q) = 1, so the answer is.
Permutations of numbers from 0 to 2 in the lexicographical order: (0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0).
In the third sample Ord(p) = 3 andOrd(q) = 5, so the answer is.
题意:n(2*10^5)个元素的排列有n!种 用Perm(x)表示字典序第x的序列(从0开始) 用Ord(排列y)表示排列y的字典序 现在输入排列p和q 求 Perm([Ord(p)+Ord(q)]%n!)
思路:对于第i位p[i] 如果它是后面的n-i+1个数中第d小的数字 那么说明比它小的d-1个数字所产生的全排列都已经计数过了。由于阶乘太大,而我们感兴趣的只是那个d-1的值,所以我们只记录每一位的那个d-1的值,最好按照类似高精度的方法,得到(Ord(p)+Ord(q))%n!。再用上述方法的逆方法,得出Perm即可,由求排名变成了求第K大。
代码:(内含有treap模板)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
#define LL long long
#define maxn 200005
class treap //treap是一颗随机的二叉排序树,可以实现插入,删除,询问前驱、后驱、排名等功能,不支持负数。各功能的时间复杂度为O(logn)
{
struct node //treap的节点定义
{
node* ch[2];
int v, r, s;
int cmp(int x)
{
if (x == v) return -1;
return(x < v ? 0 : 1);
}
};
private:
node* root; //treap的根
void updata(node* o)
{
if (!o) return;
o->s = 1;
if (o->ch[0]) o->s += o->ch[0]->s;
if (o->ch[1]) o->s += o->ch[1]->s;
}
void rateto(node* &o, int d)
{
node* k;
k = o->ch[d ^ 1];
o->ch[d ^ 1] = k->ch[d];
k->ch[d] = o;
updata(o);
updata(k);
o = k;
}
int left(node* o)
{
if (!o) return 0;
if (o->ch[0]) return o->ch[0]->s;
else return 0;
}
void mymemory(node* &o)
{
if (!o) return;
if (o->ch[0]) mymemory(o->ch[0]);
if (o->ch[1]) mymemory(o->ch[1]);
free(o);
o = 0;
}
void add(node* &o, int x)
{
if (!o)
{
o = new node();
o->ch[0] = o->ch[1] = 0;
o->v = x;
o->r = rand()*rand();
o->s = 1;
return;
}
int d = o->cmp(x);
if (d == -1) return;
else
{
add(o->ch[d], x);
updata(o);
if (o->r < o->ch[d]->r) rateto(o, d ^ 1);
}
}
void remove(node* &o, int x)
{
if (!o) return;
int d = o->cmp(x);
if (d == -1)
{
if (!(o->ch[0]))
{
node *k = o->ch[1];
free(o);
o = k;
}
else if (!(o->ch[1]))
{
node *k = o->ch[0];
free(o);
o = k;
}
else
{
int d2 = o->ch[0]->r > o->ch[1]->r ? 1 : 0;
rateto(o, d2);
remove(o->ch[d2], x);
}
}
else remove(o->ch[d], x);
updata(o);
}
bool finded(node* o, int x)
{
if (!o) return 0;
while (o)
{
int d = o->cmp(x);
if (d == -1) return 1;
else o = o->ch[d];
}
return 0;
}
int Kth(node* o, int k)
{
if (!o) return -1;
if (k == left(o) + 1) return o->v;
else
{
if (k <= left(o)) return Kth(o->ch[0], k);
else return Kth(o->ch[1], k - left(o) - 1);
}
}
int Rank(node* o, int x, int n)
{
if (!o) return -1;
int d = o->cmp(x);
if (d == -1) return n + left(o) + 1;
if (d == 0) return Rank(o->ch[0], x, n);
else return Rank(o->ch[1], x, n + left(o) + 1);
}
int Suc(node* &o, int x)
{
if (!o) return -1;
int d = o->cmp(x);
if (d == -1)
{
if (o->ch[1]) return Suc(o->ch[1], x);
else return -1;
}
else
{
int s = Suc(o->ch[d], x);
if (s != -1) return s;
if (s == -1 && o->v > x) return o->v;
else return -1;
}
}
int Pre(node* &o, int x)
{
if (!o) return -1;
int d = o->cmp(x);
if (d == -1)
{
if (o->ch[0]) return Pre(o->ch[0], x);
else return -1;
}
else
{
int s = Pre(o->ch[d], x);
if (s != -1) return s;
if (s == -1 && o->v < x) return o->v;
else return -1;
}
}
//以上为treap的功能实现部分
public:
int size()//返回该treap的内元素的个数
{
if (root) return root->s;
else return 0;
}
void insert(int x)//向treap中插元素x,如果x已经在treap中,则不操作
{
add(root, x);
}
void erase(int x)//删除treap中元素x,如果没有,则不操作
{
remove(root, x);
}
bool find(int x)//返回x是否在treap中
{
return finded(root, x);
}
int kth(int x)//返treap中的排名为x的元素的值,如果x非法,返回-1
{
if (x <= 0 || x > size()) return -1;
return Kth(root, x);
}
int rank(int x)//返回x在treap中的排名,如果x不在treap中,返回-1
{
return Rank(root, x, 0);
}
void clear()//清空treap,释放内存
{
mymemory(root);
}
int suc(int x)//返回treap中比x大的第一元素的值(后驱),如果x大于等于treap中的最大值,返回-1
{
return Suc(root, x);
}
int pre(int x)//返回treap中比x小的第一元素的值(前驱),如果x小于等于treap中的最小值,返回-1
{
return Pre(root, x);
}
treap()//定义初始化;
{
root = 0;
}
};
int a[maxn],b[maxn];
int p[maxn],q[maxn];
int c[maxn];
int main()
{
int n;
scanf("%d",&n);
treap ta,tb;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
ta.insert(a[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
tb.insert(b[i]);
}
for(int i=1;i<=n;i++)
{
p[i]=ta.rank(a[i])-1;
ta.erase(a[i]);
}
for(int i=1;i<=n;i++)
{
q[i]=tb.rank(b[i])-1;
tb.erase(b[i]);
}
memset(c,0,sizeof(c));
for(int i=n;i>=1;i--)
{
c[i]+=q[i]+p[i];
if(c[i]>=n-i+1)
{
c[i]-=n-i+1;
c[i-1]++;
}
}
treap tc;
for(int i=0;i<n;i++) tc.insert(i);
for(int i=1;i<=n;i++)
{
int tmp=tc.kth(c[i]+1);
printf("%d ",tmp);
tc.erase(tmp);
}
printf("\n");
return 0;
}
其实这道题用线段树也能做,输入的数字不大,不需要离散化都行
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define maxn 200005
using namespace std;
class segement_tree_for_balance
{
private:
struct node
{
int l,r,sum;
};
node *tree;
inline void pushup(int id)
{
tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}
void build(int id,int l,int r)
{
tree[id].l=l;
tree[id].r=r;
tree[id].sum=0;
if(l!=r)
{
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}
}
int find_num_front(int id,int x)
{
if(tree[id].l==tree[id].r) return 0;
else
{
int mid=(tree[id].l+tree[id].r)>>1;
if(x<=mid) return find_num_front(id<<1,x);
else return tree[id<<1].sum+find_num_front(id<<1|1,x);
}
}
int find_kth_num(int id,int x)
{
if(tree[id].l==tree[id].r) return tree[id].l;
else
{
if(x<=tree[id<<1].sum) return find_kth_num(id<<1,x);
else return find_kth_num(id<<1|1,x-tree[id<<1].sum);
}
}
void update(int id,int pos,int x)
{
if(tree[id].l==tree[id].r) tree[id].sum+=x;
else
{
int mid=(tree[id].l+tree[id].r)>>1;
if(pos<=mid) update(id<<1,pos,x);
else update(id<<1|1,pos,x);
pushup(id);
}
}
public:
segement_tree_for_balance(int size)
{
tree=(node*)malloc(4*size*sizeof(node));
build(1,0,size-1);
}
void insert(int x)
{
update(1,x,1);
}
void erase(int x)
{
update(1,x,-1);
}
int kth(int k)
{
return find_kth_num(1,k);
}
int rank(int x)
{
return find_num_front(1,x)+1;
}
};
int a[maxn],b[maxn];
int p[maxn],q[maxn];
int c[maxn];
int main()
{
int n;
scanf("%d",&n);
segement_tree_for_balance ta(n),tb(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
ta.insert(a[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
tb.insert(b[i]);
}
for(int i=1;i<=n;i++)
{
p[i]=ta.rank(a[i])-1;
ta.erase(a[i]);
}
for(int i=1;i<=n;i++)
{
q[i]=tb.rank(b[i])-1;
tb.erase(b[i]);
}
memset(c,0,sizeof(c));
for(int i=n;i>=1;i--)
{
c[i]+=q[i]+p[i];
if(c[i]>=n-i+1)
{
c[i]-=n-i+1;
c[i-1]++;
}
}
segement_tree_for_balance tc(n);
for(int i=0;i<n;i++) tc.insert(i);
for(int i=1;i<=n;i++)
{
int tmp=tc.kth(c[i]+1);
printf("%d ",tmp);
tc.erase(tmp);
}
printf("\n");
return 0;
}
比平衡树快了近1倍