题目链接
Let's define the sum of two permutations p and q of numbers 0, 1, ..., (n - 1) as permutation , where Perm(x) is the x-th lexicographically permutation of numbers 0, 1, ..., (n - 1) (counting from zero), and Ord(p) is the number of permutation p 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 kfollowing 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 from 0 to n - 1, separated by a space, forming permutation p.
The third line contains n distinct integers from 0 to n - 1, separated by spaces, forming permutation q.
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 and Ord(q) = 0, so the answer is .
In the second sample Ord(p) = 0 and Ord(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 and Ord(q) = 5, so the answer is .
题意:已知两个0到n-1的n个数的排列a,b。ord(a)在0到n-1的数的所有排列中的按字典序排序后的位置。ord(b)同理。输出排列x,ord(x)=(ord(a)+ord(b))%n!。
题解:首先要了解康拓展开:康拓展开。
x=a[n]*(n-1)!+a[n-1]*(n-2)!+.......a[1]*0!。,0<=a[i]<i。
由于数据范围太大,直接求出ord显然是不行的。我们可以根据a,b的康拓展开式,直接求出x的康拓展开式。
因为a[i]<=i-1。
所以,排列a康拓展开有可能的最大值为:(n-1)*(n-1)!+(n-2)*(n-2)!+.....(i-1)*(i-1)!+...0*0!
同理排列b康拓展开的最大值为:(n-1)*(n-1)!+(n-2)*(n-2)!+.....(i-1)*(i-1)!+...0*0!
将两个排列的康拓展开相加可得:n!+(n-1)*(n-1)!+(n-2)*(n-2)!+....(i-1)*(i-1)!+....0*0!
上式%n!,则可得:(n-1)*(n-1)!+(n-2)*(n-2)!+.........(i-1)*(i-1)!+....0*0!
由此可见,两个排列的康拓展开之和,最大到达n!的数量级,对n!取模过后,剩下的就是一个排列的康拓展开。我们把这个康拓展开逆展开,就解决问题了。
我们可以用类似高精度加法的方法,求出两个排列的康拓展开的和%n!的康拓展开。然后用树状数组+二分的方法,将康拓展开逆展开。复杂度为O(n*lgn*lgn)。当然也可以用平衡树或者线段树,不用二分,复杂度为O(n*lgn)。
我的代码是树状数组+二分的方法,代码如下:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> #include<string> #include<queue> #include<stack> #include<map> #include<set> #include<stdlib.h> #include<vector> #define inff 0x3fffffff #define nn 210000 #define mod 1000000007 typedef long long LL; const LL inf64=inff*(LL)inff; using namespace std; int n; int a[nn],b[nn]; int ans[nn]; int c[nn]; int dp[nn]; int lowbit(int x) { return x&(-x); } void modify(int x,int y) { for(int i=x;i<=n;i+=lowbit(i)) { c[i]+=y; } } int getsum(int x) { int sum=0; for(int i=x;i>=1;i-=lowbit(i)) sum+=c[i]; return sum; } int main() { int i; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) { scanf("%d",&a[i]); } for(i=1;i<=n;i++) { scanf("%d",&b[i]); } int ix; memset(c,0,sizeof(c)); for(i=1;i<=n;i++) { ix=getsum(a[i]); modify(a[i]+1,1); a[i]=a[i]-ix; } memset(c,0,sizeof(c)); for(i=1;i<=n;i++) { ix=getsum(b[i]); modify(b[i]+1,1); b[i]=b[i]-ix; } ix=0; for(i=n;i>=1;i--) { ans[i]=(a[i]+b[i]+ix)%(n-i+1); ix=(a[i]+b[i]+ix)/(n-i+1); } for(i=1;i<=n+1;i++) dp[i]=i; memset(c,1,sizeof(1)); int l,r,mid; for(i=1;i<=n;i++) { l=1,r=n; while(l<r) { mid=(l+r)/2; if(mid-getsum(mid)<ans[i]+1) l=mid+1; else r=mid; } ans[i]=l; modify(l,1); } for(i=1;i<=n;i++) { printf("%d%c",ans[i]-1,i==n?'\n':' '); } } return 0; }
线段树做法代码如下:
#include<algorithm> #include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<math.h> #define nn 210000 #define eps 1e-8 #define inff 0x7fffffff #define lson rt<<1,l,m #define rson rt<<1|1,m+1,r #define mod 20071027 using namespace std; typedef __int64 LL; typedef unsigned long long LLU; int n; int a[nn],b[nn],c[nn]; int L[nn*4],R[nn*4],sum[nn*4]; void push_up(int id) { sum[id]=sum[id<<1]+sum[id<<1|1]; } void build(int id,int l,int r) { L[id]=l,R[id]=r; if(l==r) { sum[id]=1; return ; } int m=(l+r)>>1; build(2*id,l,m); build(2*id+1,m+1,r); push_up(id); } void update(int id,int x) { if(L[id]==R[id]) { sum[id]=0; return ; } int m=(L[id]+R[id])>>1; if(x<=m) update(2*id,x); else update(2*id+1,x); push_up(id); } int query(int id,int l,int r) { if(L[id]>=l&&R[id]<=r) { return sum[id]; } int m=(L[id]+R[id])>>1; int re=0; if(l<=m) re+=query(2*id,l,r); if(r>m) re+=query(2*id+1,l,r); return re; } int ask(int id,int x) { if(L[id]==R[id]) { sum[id]=0; return L[id]; } int re; if(sum[2*id]>=x) re=ask(2*id,x); else re=ask(2*id+1,x-sum[2*id]); push_up(id); return re; } int main() { int i,x; while(scanf("%d",&n)!=EOF) { build(1,0,n-1); for(i=1;i<=n;i++) { scanf("%d",&x); update(1,x); a[i]=query(1,0,x); } build(1,0,n-1); for(i=1;i<=n;i++) { scanf("%d",&x); update(1,x); b[i]=query(1,0,x); } int ix=0; build(1,0,n-1); for(i=n;i>=1;i--) { c[i]=(a[i]+b[i]+ix)%(n-i+1); ix=(a[i]+b[i]+ix)/(n-i+1); } for(i=1;i<=n;i++) { printf("%d%c",ask(1,c[i]+1),i==n?'\n':' '); } } return 0; }