一个学校里老师要将班上NN个同学排成一列,同学被编号为$1~N$,他采取如下的方法:
-
先将11号同学安排进队列,这时队列中只有他一个人;
-
2-N2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1-(i -1)1−(i−1)中某位同学(即之前已经入列的同学)的左边或右边;
3.从队列中去掉M(M<N)M(M<N)个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
输入输出格式
输入格式:
第11行为一个正整数NN,表示了有NN个同学。
第2-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 121
将同学33插入至同学22右边,此时队列为:
2 3 1231
将同学44插入至同学11左边,此时队列为:
2 3 4 12341
将同学33从队列中移出,此时队列为:
2 4 1241
同学33已经不在队列中,忽略最后一条指令
最终队列:
2 4 1241
数据范围
对于20\%20%的数据,有N≤10N≤10;
对于40\%40%的数据,有N≤1000N≤1000;
对于100\%100%的数据,有N, M≤100000N,M≤100000。
不要看到这道题就认为这道题是要用队列来做,队列只能在端点处进行插入和删除操作,这道题显然不对。由题意可知这道题可以用链表来进行插入和删除操作。最开始想的时候,在第二步的删除操作时如果按照传统的链表的查找然后在删除那这道题的复杂度就是O(n * n)显然不对,后来 看了大佬们的思路,明白了,我们可以设置一个头指针,通过移动头指针来控制输出,对于删除的节点我们给他标记一下就可以了,头指针遇到要删除的节点直接跳过就可以了。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000010;
bool vis[MAXN];
struct Node{
int left,right,data;
}link[MAXN];
int main(){
int n,m,p,k,q;
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for(int i = 1; i <= n; i++){
link[i].data = i;
link[i].left = i;
link[i].right = i;
}
link[1].left = 1;
int head = 1;
for(int i = 2; i <= n; i++){
scanf("%d %d",&k, &p);
if(p == 0){
link[i].right = k;
link[i].left = link[k].left;
link[link[i].left].right = i;
link[k].left = i;
if(k == head) head = i; //更新头节点
}
else if(p == 1){
link[i].left = k;
link[i].right = link[k].right;
link[link[i].right].left = i;
link[k].right = i;
}
}
scanf("%d",&m);
for(int i = 1; i <= m; i++){
scanf("%d",&q);
vis[q] = 1;
}
int cnt = n;
bool flag = false;
while(cnt--){
if(vis[head]){
head = link[head].right;
continue;
}
if(!flag){
flag = true;
printf("%d",link[head].data);
}
else{
printf(" %d",link[head].data);
}
head = link[head].right;
}
printf("\n");
return 0;
}