Loading

块状链表

对于线性表,可以 \(O(1)\) 的访问,但是插入和删除操作是 \(O(n)\)

对于链表,可以 \(O(1)\) 的进行插入和删除,但是是 \(O(n)\) 的访问。

于是本着分块的思想,有了块状链表

大概长这个样子。每个块的大小数量级在 \(O(\sqrt n)\) , 块数的量级 \(O(\sqrt n)\)

主要有以下几种操作:

插入

(1) 分裂节点 \(O(\sqrt n)\)

(2) 在分裂点插入 \(O(\sqrt n)\)

删除

(1) 删除开头节点的后半部分 \(O(\sqrt n)\)

(2) 删除中心完整节点 \(O(\sqrt n)\)

(3) 删除结尾节点的前半部分 \(O(\sqrt n)\)

合并

为了保证正确的复杂度,要不定期的进行合并操作。

所谓合并操作,就是从第一个块开始,如果把下一个块合并过来之后大小不大于 $ \sqrt n$ ,就把两个块合并

若没有合并操作,则可能会有很多小块,导致 \(TLE\)

rope

STL 中带的块状链表,内部好像是平衡树实现的

【2020牛客国庆派对Day8】 G. Shuffle Cards

题意

\(n,m\)

初始状态为 \(1,2,3,...,n\)

\(m\) 次操作,每次操作从 \(pos\) 出开始,取 \(l\) 长度,把这一段取出放到最前面

问最后的状态

#include <bits/stdc++.h>
#include <ext/rope>

using namespace std;
using namespace __gnu_cxx;
const int N = 100010;

int main() {
	int n, m, a[N] = {0}; cin >> n >> m;
	for (int i = 0; i < n; i++) a[i] = i + 1;
	rope<int> rp(a);
	while (m--) {
		int p, s; cin >> p >> s; p--;
		rp.insert(0, rp.substr(p, s));
		rp.erase(p + s, s);
	}
	for (auto i : rp) cout << i << " ";
	return 0;
}

代码

文本编辑器

/*
 * @Author: zhl
 * @Date: 2020-11-18 11:30:27
 */
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;

const int N = 2e3 + 10, M = 2e3 + 10;

struct node {
	char s[N + 1];
	int c, l, r;
}p[M];
int id[N], idx;//可分配的编号池
char str[2000010];
int n, x, y;
void move(int k)//移动到第k个字符后面
{
	x = p[0].r;
	while (k > p[x].c) k -= p[x].c, x = p[x].r;
	y = k - 1;
}
void add(int x, int u) //将节点u插到节点x的右边
{
	p[u].r = p[x].r; p[p[u].r].l = u;
	p[x].r = u; p[u].l = x;
}
void del(int u) //删除节点u
{
	p[p[u].l].r = p[u].r;
	p[p[u].r].l = p[u].l;
	p[u].l = p[u].r = p[u].c = 0;
	id[++idx] = u;
}
void insert(int k) //在光标后面插入k个字符
{
	if (y + 1 != p[x].c) //分裂
	{
		int u = id[idx--];
		for (int i = y + 1; i < p[x].c; i++) p[u].s[p[u].c++] = p[x].s[i];
		p[x].c = y + 1;
		add(x, u);
	}
	int cur = x, i = 0;
	while (i < k) {
		int u = id[idx--];
		for (; i < k and p[u].c < N; i++) {
			p[u].s[p[u].c++] = str[i];
		}
		add(cur, u);
		cur = u;
	}
}
void remove(int k) //删除光标后的k个字符
{
	if (y + 1 + k <= p[x].c) {
		for (int i = y + 1, j = y + 1 + k; j < p[x].c; j++,i++) {
			p[x].s[i] = p[x].s[j];
		}
		p[x].c -= k;
	}
	else {
		k -= (p[x].c - y - 1);
		p[x].c = y + 1;
		while (p[x].r and k >= p[p[x].r].c) {
			k -= p[p[x].r].c;
			del(p[x].r);
		}
		int u = p[x].r;
		for (int i = 0, j = k; j < p[u].c; j++, i++)p[u].s[i] = p[u].s[j];
		p[u].c -= k;
	}
}

void get(int k) //获取光标后k个字母
{
	if (y + 1 + k <= p[x].c) {
		for (int i = y + 1; i <= y + k; i++)putchar(p[x].s[i]);
	}
	else {
		k -= (p[x].c - y - 1);
		for (int i = y + 1; i < p[x].c; i++)putchar(p[x].s[i]);
		int cur = x;
		while (p[cur].r and k >= p[p[cur].r].c) {
			k -= p[p[cur].r].c;
			for (int i = 0; i < p[p[cur].r].c; i++)putchar(p[p[cur].r].s[i]);
			cur = p[cur].r;
		}
		int u = p[cur].r;
		for (int i = 0; i < k; i++)putchar(p[u].s[i]);
	}
	puts("");
}
void prev() //光标前移
{
	if (y) y--;
	else {
		x = p[x].l;
		y = p[x].c - 1;
	}
}
void next() //光标后移
{
	if (y != p[x].c - 1) {
		y++;
	}
	else {
		x = p[x].r;
		y = 0;
	}
}

void merge() //关键操作,将长度较短的合并,保持正确的复杂度
{
	for (int i = p[0].r; i; i = p[i].r) {
		while (p[i].r and p[i].c + p[p[i].r].c < N) {
			int r = p[i].r;	
			for (int ii = p[i].c, j = 0; j < p[r].c; ii++, j++) {
				p[i].s[ii] = p[r].s[j];
			}
			if (x == r) x = i, y += p[i].c;
			p[i].c += p[r].c;
			del(r);
		}
	}
}
int main() {
	for (int i = 1; i < M; i++) id[++idx] = i;
	scanf("%d", &n);
	char op[10];

	str[0] = '>';
	insert(1);  // 插入哨兵
	move(1);  // 将光标移动到哨兵后面

	while (n--)
	{
		int a;
		scanf("%s", op);
		if (!strcmp(op, "Move"))
		{
			scanf("%d", &a);
			move(a + 1);
		}
		else if (!strcmp(op, "Insert"))
		{
			scanf("%d", &a);
			int i = 0, k = a;
			while (a)
			{
				str[i] = getchar();
				if (str[i] >= 32 && str[i] <= 126) i++, a--;
			}
			insert(k);
			merge();
		}
		else if (!strcmp(op, "Delete"))
		{
			scanf("%d", &a);
			remove(a);
			merge();
		}
		else if (!strcmp(op, "Get"))
		{
			scanf("%d", &a);
			get(a);
		}
		else if (!strcmp(op, "Prev")) prev();
		else next();
	}
}


rope 版本

/*
 * @Author: zhl
 * @Date: 2020-11-18 10:28:37
 */
#include <bits/stdc++.h>
#include <ext/rope>

using namespace std;
using namespace __gnu_cxx;
char str[2000010];
int main(){
    rope<char> rp;int pos = 0;
    int n;
	scanf("%d", &n);
	char op[10];
	while (n--)
	{
		int a;
		scanf("%s", op);
		if (!strcmp(op, "Move"))
		{
			scanf("%d", &pos);
		}
		else if (!strcmp(op, "Insert"))
		{
			scanf("%d", &a);
            str[a] = '\0';// Important!!!
			int i = 0, k = a;
			while (a)
			{
				str[i] = getchar();
				if (str[i] >= 32 && str[i] <= 126) i++, a--;
			}
			rp.insert(pos,str);
		}
		else if (!strcmp(op, "Delete"))
		{
			scanf("%d", &a);
			rp.erase(pos,a);
		}
		else if (!strcmp(op, "Get"))
		{
			scanf("%d", &a);a--;
			for(int i = pos;i <= pos + a;i++)putchar(rp[i]);
            puts("");
		}
		else if (!strcmp(op, "Prev")) pos--;
		else pos++;
	}
}
posted @ 2020-11-19 22:08  —O0oO-  阅读(622)  评论(0编辑  收藏  举报