排列
题目描述:
有1~N的数,已知每一个位置的逆序对,求原序列。
输入格式:
第一行输入一个正整数N。
第二行输入N个正整数,表示Pi(逆序对数前缀和)
输出格式:
输出一行,共有N个数,表示原排列Ai
数据范围:
对于前10%的数据:N<=10。
对于前30%的数据:N<=1000。
对于100%的数据:n<=100000。
解析:
一题多解,共有三种方法:二分+树状数组O(N (log N)^2)、区间线段树O(N log N),权值线段树(N log N)。
先说说题解做法(复杂度反而最高.....)二分+树状数组。首先做差得到每一个位置的逆序对数。我们考虑正着从第一个位置来做会很复杂,于是我们就可以从第N个位置往前处理,确定最后一个数后倒着确定每一个数。我们先把每一个数个数赋为1,存入树状数组。对于每一个位置,我们二分符合条件的数,然后用将这个数的个数赋为0,用树状数组做单点修改,这样以后就不会再用这个数。
权值线段树的做法跟题解做法一样,不断找第Ki大的数,只是用权值线段树会少一个log,复杂度更低。
区间线段树的做法是找规律,我们不难发现每次都是将最大的数放入当前数列中最后一个逆序对数为0的位置,于是我们就可以用线段树寻找最后一个对数为0的位置,将它修改为INF,再将该位置后面的位置的对数全部减1,这样就不用再考虑这个位置,然后重复这个操作。
代码 (二分+树状数组)
#include <bits/stdc++.h>
using namespace std;
const int INF=1e7;
const int Max=101000;
int n,pos;
int S[Max];
int num[Max],sum[Max];
int ans[Max],way[Max];
int tree[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline int lowbit(int x)
{
return x&(-x);
}
inline void add(int x,int num)
{
while(x<=n)
{
tree[x]+=num;
x+=lowbit(x);
}
}
inline int search(int pos)
{
int s=0;
while(pos>0)
{
s+=tree[pos];
pos-=lowbit(pos);
}
return s;
}
int main()
{
//freopen("premu.in","r",stdin);
//freopen("premu.out","w",stdout);
n=get_int();
for(register int i=1;i<=n;i++) S[i]=get_int();
for(register int i=1;i<=n;i++) num[i]=S[i]-S[i-1];
for(int i=1;i<=n;i++) add(i,1);
for(int i=n;i>=1;i--)
{
int l=1,r=n,mid,x=i-num[i];
while(l<=r)
{
mid=(l+r)>>1;
if(search(mid)<x) l=mid+1;
else r=mid-1;
}
ans[i]=r+1;
add(r+1,-1);
}
for(register int i=1;i<=n;i++) cout<<ans[i]<<" ";
return 0;
}
代码(权值线段树)
#include <bits/stdc++.h>
using namespace std;
const int INF=1e7;
const int Max=101000;
int n,pos;
int add[Max*4],S[Max];
int num[Max],sum[Max];
struct shu{int l,r,sum;};
shu tree[Max*4];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int root,int L,int R)
{
tree[root].l=L;
tree[root].r=R;
if(L==R)
{
tree[root].sum=1;
return;
}
int mid=(L+R)>>1;
build(root<<1,L,mid);
build(root<<1|1,mid+1,R);
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
}
inline void Q(int root,int L,int R,int id)
{
if(L==R)
{
tree[root].sum=0;
pos=L;
return;
}
int mid=(L+R)>>1;
if(tree[root<<1|1].sum>=id&&tree[root<<1|1].sum>0) Q(root<<1|1,mid+1,R,id);
else if(tree[root<<1].sum!=0) Q(root<<1,L,mid,id-tree[root<<1|1].sum);
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
}
int main()
{
//freopen("premu.in","r",stdin);
//freopen("premu.out","w",stdout);
n=get_int();
for(register int i=1;i<=n;i++) S[i]=get_int();
for(register int i=1;i<=n;i++) sum[i]=S[i]-S[i-1];
build(1,1,n);
for(int i=n;i>=1;i--)
{
Q(1,1,n,sum[i]+1);
num[i]=pos;
}
for(register int i=1;i<=n;i++) cout<<num[i]<<" ";
return 0;
}
区间线段树:
#include <bits/stdc++.h>
using namespace std;
const int INF=1e7;
const int Max=101000;
int n,pos;
int add[Max*4],S[Max];
int num[Max],sum[Max];
struct shu{int l,r,minn;};
shu tree[Max*4];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int root,int L,int R)
{
tree[root].l=L;
tree[root].r=R;
if(L==R)
{
tree[root].minn=sum[L];
return;
}
int mid=(L+R)>>1;
build(root<<1,L,mid);
build(root<<1|1,mid+1,R);
tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}
inline void pushdown(int root)
{
tree[root<<1].minn-=add[root];
tree[root<<1|1].minn-=add[root];
add[root<<1]+=add[root];
add[root<<1|1]+=add[root];
add[root]=0;
}
inline void Q(int root,int L,int R)
{
if(L==R&&tree[root].minn==0)
{
tree[root].minn=INF;
pos=L;
return;
}
pushdown(root);
int mid=(L+R)>>1;
if(tree[root<<1|1].minn==0) Q(root<<1|1,mid+1,R);
else Q(root<<1,L,mid);
tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}
inline void change(int root,int L,int R,int l,int r)
{
if(l<=L&&r>=R)
{
tree[root].minn--;
add[root]++;
return;
}
int mid=(L+R)>>1;
if(l<=mid) change(root<<1,L,mid,l,r);
if(r>mid) change(root<<1|1,mid+1,R,l,r);
tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}
int main()
{
//freopen("premu.in","r",stdin);
//freopen("premu.out","w",stdout);
n=get_int();
for(register int i=1;i<=n;i++) S[i]=get_int();
for(register int i=1;i<=n;i++) sum[i]=S[i]-S[i-1];
build(1,1,n);
for(register int i=n;i>=1;i--)
{
Q(1,1,n);
num[pos]=i;
if(pos<n) change(1,1,n,pos+1,n);
}
for(register int i=1;i<=n;i++) cout<<num[i]<<" ";
return 0;
}