题目描述
一个学校里老师要将班上 NN 个同学排成一列,同学被编号为 1\sim N1∼N,他采取如下的方法:
-
先将 11 号同学安排进队列,这时队列中只有他一个人;
-
2-N2−N 号同学依次入列,编号为 ii 的同学入列方式为:老师指定编号为 ii 的同学站在编号为 1\sim(i-1)1∼(i−1) 中某位同学(即之前已经入列的同学)的左边或右边;
-
从队列中去掉 M(M<N)M(M<N) 个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
输入格式
第 11 行为一个正整数 NN,表示了有 NN 个同学。
第 2\sim N2∼N行,第 ii 行包含两个整数 k,pk,p,其中 kk 为小于 ii 的正整数,pp 为 00 或者 11。若 pp 为00,则表示将 ii 号同学插入到 kk 号同学的左边,pp 为 11 则表示插入到右边。
第 N+1N+1 行为一个正整数 MM,表示去掉的同学数目。
接下来 MM 行,每行一个正整数 xx,表示将 xx 号同学从队列中移去,如果 xx 号同学已经不在队列中则忽略这一条指令。
输出格式
11 行,包含最多 NN 个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。
输入输出样例
输入 #1复制
4 1 0 2 1 1 0 2 3 3
输出 #1复制
2 4 1
说明/提示
样例解释:
将同学 22 插入至同学 11 左边,此时队列为:
2 1
将同学 33 插入至同学 22 右边,此时队列为:
2 3 1
将同学 44 插入至同学 11 左边,此时队列为:
2 3 4 1
将同学 33 从队列中移出,此时队列为:
2 4 1
同学 33 已经不在队列中,忽略最后一条指令
最终队列:
2 4 1
数据范围
对于 20\%20% 的数据,有 1\leq N\leq 101≤N≤10;
对于 40\%40% 的数据,有 1\leq N\leq 10001≤N≤1000;
对于 100\%100% 的数据,有 1\leq N,M\leq1000001≤N,M≤100000。
解法一:用数组模拟双向链表
#include<iostream>
#include<cstdio>
using namespace std;
int L[100005],R[100005],flag[100005];//L[i]表示i号同学左侧同学的编号
int main()
{
int n,m,left=1;
scanf("%d",&n);
L[1]=0,R[1]=0;
for(int i=2;i<=n;i++)
{
int temp1,temp2;
scanf("%d %d",&temp1,&temp2);
if(temp2==0) //i插到temp1左侧
{
if(L[temp1]==0) //temp1 左侧没有人
{
L[temp1]=i;//temp1 左侧的人是 i
R[i]=temp1;//i 右侧的人是 temp1
}
else//temp1 左侧有人
{
L[i]=L[temp1];//i 左侧的人是temp1 左侧的人
R[i]=temp1;//i 右侧的人是 temp1
R[L[temp1]]=i;//temp1 原来左侧的人的现在右侧的人是 i
L[temp1]=i;//temp1 的左侧的人是 i
}
if(L[i]==0) //i左侧没有人
left=i; //记录最左侧同学的编号
}
else //右侧
{
if(R[temp1]==0)
{
R[temp1]=i;
L[i]=temp1;
}
else
{
L[i]=temp1;
R[i]=R[temp1];
L[R[temp1]]=i;
R[temp1]=i;
}
}
}
scanf("%d",&m);
for(int i=0;i<m;i++)
{
int temp1;
scanf("%d",&temp1);
flag[temp1]=1;//给出队的同学打上标记
}
while(left!=0) //打印编号
{
if(!flag[left])
printf("%d ",left);
left=R[left];
}
return 0;
}
解法二:用数组模拟双向链表
#include<stdio.h>
int n,m;
struct node{
int l,r;
int id;
}a[1000005];
int main(){
scanf("%d",&n);
for(int i=2;i<=n;i++){//从2开始,将i号学生插到k号的一边
int k,p;
scanf("%d%d",&k,&p);//第几个人的哪一边
if(p==1){//插在右边
//这四行是在将a[k],a[k].r,a[i]之间两两建立双向联系
a[i].r=a[k].r;//i右边的人是原来k右边的人
a[i].l=k;//i左边的人是k
a[a[k].r].l=i;//原来k右边的人现在左边的人是i
a[k].r=i;//k右边的人是i
}
else{//插在左边,l,r对调即可
a[i].l=a[k].l;
a[i].r=k;
a[a[k].l].r=i;
a[k].l=i;
}
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
int x;
scanf("%d",&x);
a[x].id=1;//标记要删除的人
}
int now;
for(int i=1;i<=n;i++) if(a[i].l==0&&a[i].id!=1) now=i;//如果没有赋值自动赋为0
//找到整个队列的第一个人
//如果左边没人且这个人不会被删,将其赋给now
while(now){//如果now存在的话
if(a[now].id==0) printf("%d ",now);
now=a[now].r;//下一个
}
return 0;
}