Problem Description
度度熊正在学习双端队列,他对其翻转和合并产生了很大的兴趣。
初始时有 N 个空的双端队列(编号为 1 到 N ),你要支持度度熊的 Q 次操作。
①1 u w val 在编号为 u 的队列里加入一个权值为 val 的元素。(w=0 表示加在最前面,w=1 表示加在最后面)。
②2 u w 询问编号为 u 的队列里的某个元素并删除它。( w=0 表示询问并操作最前面的元素,w=1 表示最后面)
③3 u v w 把编号为 v 的队列“接在”编号为 u 的队列的最后面。w=0 表示顺序接(队列 v 的开头和队列 u 的结尾连在一起,队列v 的结尾作为新队列的结尾), w=1 表示逆序接(先将队列 v 翻转,再顺序接在队列 u 后面)。且该操作完成后,队列 v 被清空。
初始时有 N 个空的双端队列(编号为 1 到 N ),你要支持度度熊的 Q 次操作。
①1 u w val 在编号为 u 的队列里加入一个权值为 val 的元素。(w=0 表示加在最前面,w=1 表示加在最后面)。
②2 u w 询问编号为 u 的队列里的某个元素并删除它。( w=0 表示询问并操作最前面的元素,w=1 表示最后面)
③3 u v w 把编号为 v 的队列“接在”编号为 u 的队列的最后面。w=0 表示顺序接(队列 v 的开头和队列 u 的结尾连在一起,队列v 的结尾作为新队列的结尾), w=1 表示逆序接(先将队列 v 翻转,再顺序接在队列 u 后面)。且该操作完成后,队列 v 被清空。
Input
有多组数据。
对于每一组数据,第一行读入两个数 N 和 Q。
接下来有 Q 行,每行 3~4 个数,意义如上。
N≤150000,Q≤400000
1≤u,v≤N,0≤w≤1,1≤val≤100000
所有数据里 Q 的和不超过500000
对于每一组数据,第一行读入两个数 N 和 Q。
接下来有 Q 行,每行 3~4 个数,意义如上。
N≤150000,Q≤400000
1≤u,v≤N,0≤w≤1,1≤val≤100000
所有数据里 Q 的和不超过500000
Output
对于每组数据的每一个操作②,输出一行表示答案。
注意,如果操作②的队列是空的,就输出−1且不执行删除操作。
注意,如果操作②的队列是空的,就输出−1且不执行删除操作。
Sample Input
2 10
1 1 1 23
1 1 0 233
2 1 1
1 2 1 2333
1 2 1 23333
3 1 2 1
2 2 0
2 1 1
2 1 0
2 1 1
1 1 1 23
1 1 0 233
2 1 1
1 2 1 2333
1 2 1 23333
3 1 2 1
2 2 0
2 1 1
2 1 0
2 1 1
Sample Output
23
-1
2333
233
23333
-1
2333
233
23333
提示
由于读入过大,C/C++ 选手建议使用读入优化。
一个简单的例子:
void read(int &x){
char ch = getchar();x = 0;
for (; ch < '0' || ch > '9'; ch = getchar());
for (; ch >='0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
}
一个简单的例子:
void read(int &x){
char ch = getchar();x = 0;
for (; ch < '0' || ch > '9'; ch = getchar());
for (; ch >='0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
}
题意
实现双向队列,要求的功能有:维护N个双向队列,在首/尾部进行插入/删除,以及将一个队列顺序/逆序附到另一个队列尾部,并且清空原序列。
分析
为了避免双向队列颠倒的复杂度,其实完全可以忽略节点的先后顺序,把双向队列想象成一个所有节点入度出度都为1的连通图,那么对链表所有的操作,都可以看作是在图上的加边减边操作,因此,只需要维护每个节点的两条边就行了。
具体的来说,队列加点=连通图中新增一个节点并连一条边到指定的边界;队列删点=图删边;队列附加=两个连通图之间加一条边,连接两个边界点。
因为总是在队列首尾操作,也就是图的边界点,因此记录下每个连通图的两个边界,每次只需要找到图上边界点的相邻的点进行操作,并在操作完后,更新连通图的边界点。
具体的来说,队列加点=连通图中新增一个节点并连一条边到指定的边界;队列删点=图删边;队列附加=两个连通图之间加一条边,连接两个边界点。
因为总是在队列首尾操作,也就是图的边界点,因此记录下每个连通图的两个边界,每次只需要找到图上边界点的相邻的点进行操作,并在操作完后,更新连通图的边界点。
注意
空队列不能被删除,但可以添加一个非空队列到他的后面
代码
#include<stdio.h> #include<memory.h> int head[150005], tail[150005]; int n, q; int id = 0; //双向链表节点 struct thing { int v; int t1, t2;//不必记录顺序 thing() :v(0), t1(-1), t2(-1) {} void conn(int x) { if (t1 == -1)t1 = x; else t2 = x; } void brk(int x) { if (t1 == x)t1 = -1; else if (t2 == x)t2 = -1; } }node[400005]; //头部插入 void qaddf(int qid, int nid) { if (head[qid] == -1) { head[qid] = nid; tail[qid] = nid; } else { int h = head[qid]; node[nid].conn(h); node[h].conn(nid); head[qid] = nid; } } //尾部插入 void qadde(int qid, int nid) { if (head[qid] == -1) { head[qid] = nid; tail[qid] = nid; } else { int t = tail[qid]; node[nid].conn(t); node[t].conn(nid); tail[qid] = nid; } } //头部删除 int delf(int qid) { int s = head[qid]; if (s == -1)return -1; if (head[qid] == tail[qid]) { head[qid] = -1; tail[qid] = -1; return node[s].v; } int next = (node[s].t1 != -1 ? node[s].t1 : node[s].t2); node[next].brk(s); head[qid] = next; return node[s].v; } //尾部删除 int dele(int qid) { int s = tail[qid]; if (s == -1)return -1; if (head[qid] == tail[qid]) { head[qid] = -1; tail[qid] = -1; return node[s].v; } int next = (node[s].t1 != -1 ? node[s].t1 : node[s].t2); node[next].brk(s); tail[qid] = next; return node[s].v; } //顺序连接 void linkf(int q1, int q2) { if (head[q2] == -1)return; if (head[q1] != -1) { node[tail[q1]].conn(head[q2]); node[head[q2]].conn(tail[q1]); tail[q1] = tail[q2]; } else { head[q1] = head[q2]; tail[q1] = tail[q2]; } head[q2] = -1; tail[q2] = -1; } //逆序连接 void linke(int q1, int q2) { if(head[q2]==-1)return; if (head[q1] != -1) { node[tail[q1]].conn(tail[q2]); node[tail[q2]].conn(tail[q1]); tail[q1] = head[q2]; } else { tail[q1] = head[q2]; head[q1] = tail[q2]; } head[q2] = -1; tail[q2] = -1; } //简易读外挂 void getn(int&x) { x = 0; char ch; do ch = getchar(); while (ch<'0' || ch>'9'); do { x = x * 10 + ch - '0'; ch = getchar(); } while ('0' <= ch&&ch <= '9'); } int main() { int ipt[4]; while (~scanf("%d %d", &n, &q)) { id = 0; memset(head, -1, sizeof head); memset(tail, -1, sizeof tail); for (int i = 0; i<q; ++i) { getn(ipt[0]); switch (ipt[0]) { case 1://在队列ipt[1]中插入ipt[3]到头/尾部(ipt[2]=0/1) getn(ipt[1]); getn(ipt[2]); getn(ipt[3]); node[++id].v = ipt[3]; node[id].t1 = -1; node[id].t2 = -1; if (ipt[2] == 0) { qaddf(ipt[1], id); } else { qadde(ipt[1], id); } break; case 2://在队列ipt[1]中删除头/尾部(ipt[2]=0/1) getn(ipt[1]); getn(ipt[2]); if (ipt[2] == 0) { printf("%d ", delf(ipt[1])); } else { printf("%d ", dele(ipt[1])); } break; case 3://将队列ipt[2]正/反接到ipt[1]后面(ipt[3]=0/1) getn(ipt[1]); getn(ipt[2]); getn(ipt[3]); if (ipt[3] == 0) linkf(ipt[1], ipt[2]); else linke(ipt[1], ipt[2]); break; default: break; } } } } #define tree * ** *** ||