题单
序言
在一个数列中高效插入或删除一个元素,链表毫无疑问是最好的选择。
P1160 队列安排
思路
- 根据数据范围 1 < M ≤ N ≤ 1 0 5 1<M\leq N\leq10^5 1<M≤N≤105 和题目要求的高效插入和删除元素可知,需要用链表。
- 关键是如何实现 O ( 1 ) \mathcal O(1) O(1) 或 O ( log n ) \mathcal O(\log n) O(logn) 地访问某个学生在链表中的存储下标。可以用 f i d [ k ] fid[k] fid[k] 储存编号为 k k k 的学生在链表中的存储下标。当然若 k k k 过大,可以用 map 去存。具体的操作,需要注意地是,要特殊处理一下边界。
- 时间复杂度为 O ( n + m ) \mathcal O(n + m) O(n+m)。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 100;
int cnt, n, m, vis[maxn], fid[maxn], head;
struct linklist
{
int id, prev, next;
} node[maxn];
int main ()
{
scanf ("%d", &n);
node[++ cnt] = (linklist) {1, -1, -1};
fid[1] = cnt, head = 1;
for (int i = 2; i <= n; i++)
{
int k, p;
scanf ("%d %d", &k, &p);
fid[i] = ++ cnt;
if (p == 0)
{
if (node[fid[k]].prev != -1)
node[node[fid[k]].prev].next = cnt;
node[cnt] = (linklist) {i, node[fid[k]].prev, fid[k]};
node[fid[k]].prev = cnt;
}
else
{
if (node[fid[k]].next != -1)
node[node[fid[k]].next].prev = cnt;
node[cnt] = (linklist) {i, fid[k], node[fid[k]].next};
node[fid[k]].next = cnt;
}
if (node[cnt].prev == -1) head = cnt;
}
scanf ("%d", &m);
for (int i = 1; i <= m; i++)
{
int x;
scanf ("%d", &x);
if (vis[x]) continue; vis[x] = 1;
if (node[fid[x]].prev == -1 && node[fid[x]].next != -1)
node[node[fid[x]].next].prev = -1, head = node[fid[x]].next;
else if (node[fid[x]].next == -1 && node[fid[x]].prev != -1)
node[node[fid[x]].prev].next = -1;
else if (node[fid[x]].prev != -1 && node[fid[x]].next != -1)
{
int previ = node[fid[x]].prev, nexti = node[fid[x]].next;
node[previ].next = nexti, node[nexti].prev = previ;
}
else return 0;
}
for (int i = head; i != -1; i = node[i].next)
printf ("%d ", node[i].id);
return 0;
}
收获
- 知道了如何 O ( 1 ) \mathcal O(1) O(1) 或 O ( log n ) \mathcal O(\log n) O(logn) 地在链表中添加或删除一个元素,也就是离散化的应用。
P1996 约瑟夫问题
思路
- 是个环,于是需要将链表的首尾连接起来。然后按照题意,模拟删除即可。
- 时间复杂度为 O ( n m ) \mathcal O(nm) O(nm)。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
int n, m, cnt, p = 1, cur = 1;
struct linklisti
{
int id, prev, next;
} node[maxn];
int main ()
{
scanf ("%d %d", &n, &m);
for (int i = 2; i < n; i++)
node[i] = (linklisti) {i, i - 1, i + 1};
node[1] = (linklisti) {1, n, 2};
node[n] = (linklisti) {n, n - 1, 1};
while (n)
{
if (p == m)
{
printf ("%d ", node[cur].id);
node[node[cur].prev].next = node[cur].next;
node[node[cur].next].prev = node[cur].prev;
p = 1, n --, cur = node[cur].next;
}
else p ++, cur = node[cur].next;
}
return 0;
}
收获
- 见识到了环形链表。