【BZOJ 1014】【JSOI 2008】火星人prefix

看了《Hash在信息学竞赛中的一类应用》中的例题3,这道题很类似啊,只不过没有删点和区间翻转。

用Splay维护字符串哈希,加点改点什么的就不用说了,查询时二分答案,这样时间复杂度是$O(mlog^2 n)$的

论文的例题3中删点很简单,和插点一样,不用说了,区间翻转只要打一个翻转标记,维护正序hash和逆序hash,翻转时交换两个hash值即可。

对拍终于成功了QAQ,插点时孩子不认父亲TwT又手残了~

《Hash在信息学竞赛中的一类应用》中还提到了块状链表的做法,都很易懂:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 100003;
const int p = 9875321;

int P[N];
struct node *null;
struct node {
	node *ch[2], *fa;
	int k, s, ha;
	node (int _k = 0) {k = _k; s = 1; ha = _k; ch[0] = ch[1] = fa = null;}
	void setc(node *r, bool c) {this->ch[c] = r; r->fa = this;}
	bool pl() {return fa->ch[1] == this;}
	void count() {
		s = ch[0]->s + ch[1]->s + 1;
		ha = ((ch[0]->ha + 1ll * k * P[ch[0]->s]) % p + 1ll * ch[1]->ha * P[ch[0]->s + 1] % p) % p;
	}
} *root;

int n, m;
char s[N];

namespace Splay {
	node *Build(int l, int r) {
		if (l > r) return null;
		int mid = (l + r) >> 1;
		node *t = new node(s[mid] - 'a');
		t->ch[0] = Build(l, mid - 1); t->setc(t->ch[0], 0);
		t->ch[1] = Build(mid + 1, r); t->setc(t->ch[1], 1);
		t->count();
		return t;
	}
	void init() {
		P[0] = 1; for(int i = 1; i < N; ++i) P[i] = P[i - 1] * 26 % p;
		null = new node; null->s = 0; null->ch[0] = null->ch[1] = null->fa = null;
		scanf("%s", s + 1); n = strlen(s + 1); root = Build(1, n);
	}
	void rotate(node *r) {
		node *f = r->fa;
		bool c = r->pl();
		if (f != root) f->fa->setc(r, f->pl());
		else root = r, r->fa = null;
		f->setc(r->ch[!c], c);
		r->setc(f, !c);
		f->count();
	}
	void splay(node *r, node *tar = null) {
		for(; r->fa != tar; rotate(r))
			if (r->fa->fa != tar) rotate(r->pl() == r->fa->pl() ? r->fa : r);
		r->count();
	}
	node *kth(int k) {
		node *r = root;
		while (1) {
			if (r->ch[0]->s >= k) r = r->ch[0];
			else if (r->ch[0]->s + 1 >= k) return r;
			else k -= (r->ch[0]->s + 1), r = r->ch[1];
		}
	}
	int hash(int l, int r) {
		if (l == 1 && r == n) return root->ha;
		else if (l == 1) {splay(kth(r + 1)); return root->ch[0]->ha;}
		else if (r == n) {splay(kth(l - 1)); return root->ch[1]->ha;}
		else {splay(kth(l - 1)); splay(kth(r + 1), root); return root->ch[1]->ch[0]->ha;}
	}
	void QQ(int l, int r) {
		int left = 0, right = root->s - max(l, r) + 1, mid;
		while (left < right) {
			mid = (left + right + 1) >> 1;
			if (hash(l, l + mid - 1) == hash(r, r + mid - 1)) left = mid;
			else right = mid - 1;
		}
		printf("%d\n", left); return;
	}
	void RR(int k, int num) {
		node *r = kth(k);
		r->k = num;
		splay(r);
	}
	void II(int k, int num) {
		if (k == 0) {
			node *r = root;
			while (r->ch[0] != null) r = r->ch[0];
			r->setc(new node(num), 0);
			splay(r->ch[0]);
		} else {
			splay(kth(k));
			if (k == n) {
				root->setc(new node(num), 1);
				splay(root->ch[1]);
			} else {
				splay(kth(k + 1), root);
				root->ch[1]->setc(new node(num), 0);
				splay(root->ch[1]->ch[0]);
			}
		}
		++n;
	}
}

int main() {
	Splay::init();
	scanf("%d", &m);
	char c; int x, y;
	while (m--) {
		for(c = getchar(); c < 'A' || c > 'Z'; c = getchar());
		switch (c) {
			case 'Q':
				scanf("%d%d", &x, &y);
				Splay::QQ(x, y);
			break;
			case 'R':
				scanf("%d", &x); for(c = getchar(); c < 'a' || c > 'z'; c = getchar());
				Splay::RR(x, c - 'a');
			break;
			case 'I':
				scanf("%d", &x); for(c = getchar(); c < 'a' || c > 'z'; c = getchar());
				Splay::II(x, c - 'a');
			break;
		}
	}
	return 0;
}

对拍大法好~

posted @ 2016-05-21 21:59  abclzr  阅读(222)  评论(0编辑  收藏  举报