题目大意
一个队列,一开始为空,有n个人按顺序去插队,每个人用一对形如 (Posi,Vali) 的数字表示,意思是第i个人插到 Posi 位置的后面,这个人的值是 Vali ,问最后插好队后每个人的值一次是多少。
分析
一开始想到了从后往前考虑,比如最后一个人是一定能确定座位的,再看倒数第二个他的插入位置之和他后面有多少个人会插入到他前面有关(包括他所要插入的位置),但是我们并不能直接的知道他后面的人有多少个插到他前面,在我们逆序遍历的过程中,我们是逐个确定每一个人的位置的,假设第i个人后面的都确定好了之后,对于第i个人,他前面有x个人已经插入了,那么我们应该将i的位置再向后推移x个,但直接出现了一个问题,在推移的过程中还可能遇到已经插入的人,那就还需要再往后推移,然后就卡在了这个地方。看了题解之后发现其实我们可以换个角度看这个问题,我们不从他前面有多少个人考虑,而从他前面有多少个空位考虑他后面的人的位置是已经确定了的,那么将这些人去掉之后第i个人的座位就应该是他一开始应该插入的位置,那么将他插入到第pos[i]个空位的位置就行了,这样就避免了从人考虑的过程中人数在动态变换的麻烦。
总结
通过对一些边界情况或简单情况的考虑我们或许能够发现一些规律从而归纳出解决题目的方法。
从多个角度去看一个问题
代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
using namespace std;
const int MAXN=200005;
int pos[MAXN],val[MAXN];//pos[i]表示第i个人要插到pos[i]的后面去
int n;
int ans[MAXN];//保存最后每个座位上的值是多少
int blank[MAXN*4];
void Init()
{
memset(blank,0,sizeof(blank));
}
void Pushup(int rt)//节点rt一下的信息已经维护,现在将信息上传
{
blank[rt]=blank[rt*2]+blank[rt*2+1];
}
void Build(int l,int r,int rt)
{
if(l==r){blank[rt]=1;return ;}
int m=(l+r)/2;
Build(l,m,rt*2);
Build(m+1,r,rt*2+1);
Pushup(rt);
}
void Update(int x,int val,int l,int r,int rt)//在线段l到r的第x个空穴插入val
{
if(l==r){ans[l]=val;blank[rt]=0;return ;}
int m=(l+r)/2;
if(x<=blank[rt*2])Update(x,val,l,m,rt*2);
else Update(x-blank[rt*2],val,m+1,r,rt*2+1);
Pushup(rt);
}
void Print()
{
for(int i=1;i<=n;i++)
{
cout<<ans[i]<<" ";
}
cout<<endl;
}
void Work()
{
Init();
int loc;
Build(1,n,1);
for(int i=n;i>=1;i--)
{
loc=pos[i]+1;
Update(loc,val[i],1,n,1);
}
Print();
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%d%d",&pos[i],&val[i]);
}
Work();
}
return 0;
}