洛谷P1160——佇列安排【連結串列,模擬】
一個學校里老師要將班上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;
}