【NOIP模拟】 (11.3) T2 排列

20 篇文章 1 订阅
18 篇文章 0 订阅

排列

题目描述:
       有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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值