【poj2182】【poj2828】树状数组/线段树经典模型:逆序查找-空位插入法

时间:2022-04-29 19:25:23

poj2182题意:有一个1~n的排列,现在给定每个人前面有多少个人的编号比他大,求这个排列是什么。n<=8000

poj2182题解:

逆序做,可以确定二分最后一个是什么,然后删除这个数。树状数组维护每个数前面有多少个数比它小。

poj2828题意:有 n 个人排队买票,他们依次到来,第 i 个人来的时候会站在第pos[i]个人后面,并且他的编号为v[i]。
求最后的队列中每个位置人的编号。

poj2828题解:

来一个例子模拟:

0 (3) //编号为3的人插入第0个人后面

1 (2)

1 (1)

0 (4)

假设已经知道了最后的答案:

(4)(3)(1)(2)

类比上一题,我们可以逆序做,然后对于当前的最后一个人,它前面一定有且只有pos[i]个空位,就转化为上一题了。一但确定了一个人,那个空位就被填了,在树状数组上更新一下。

 //poj2182
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std; const int N=;
int n,a[N],c[N],ans[N]; void add(int x,int d)
{
for(int i=x;i<=n;i+=(i&(-i))) c[i]+=d;
}
int getsum(int x)
{
int ans=;
for(int i=x;i>=;i-=(i&(-i))) ans+=c[i];
return ans;
} int main()
{
freopen("a.in","r",stdin);
scanf("%d",&n);
memset(c,,sizeof(c));
for(int i=;i<=n;i++) add(i,);
a[]=;
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
}
int l,r,mid;
for(int i=n;i>=;i--)
{
l=,r=n;
while(l<r)
{
mid=(l+r+)/;
if(getsum(mid-)>a[i]) r=mid-;
else l=mid;
}
ans[i]=l;
add(l,-);
}
for(int i=;i<=n;i++) printf("%d\n",ans[i]);
return ;
}

poj2182

 //poj2828
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std; const int N=;
int n,a[N],val[N],c[N],ans[N]; void add(int x,int d)
{
for(int i=x;i<=n;i+=(i&(-i))) c[i]+=d;
}
int getsum(int x)
{
int ans=;
for(int i=x;i>=;i-=(i&(-i))) ans+=c[i];
return ans;
} int main()
{
freopen("a.in","r",stdin);
while(scanf("%d",&n)!=EOF)
{
memset(c,,sizeof(c));
for(int i=;i<=n;i++) add(i,);
for(int i=;i<=n;i++) scanf("%d%d",&a[i],&val[i]);
int l,r,mid;
for(int i=n;i>=;i--)
{
l=,r=n;
while(l<r)
{
mid=(l+r+)/;
if(getsum(mid-)>a[i]) r=mid-;
else l=mid;
}
ans[l]=i;
add(l,-);
}
for(int i=;i<=n;i++) printf("%d ",val[ans[i]]);printf("\n");
} return ;
}

poj2828