bzoj1014 火星人prefix

火星人prefix

题目背景:

bzoj1014

分析:我有一句XXX不知道当不当讲,但是真的太恶心了!!!

这道题本来的做法是对于每一个节点,维护以它为根的子树的字符串的hash值就可以了,我是用无旋treap来实现的,然后每一次合并和分割的时候,就可以直接update像上维护即可,但是当时开始的时候我的代码不断地T飞,然后我尝试优化了很多波的常数,最后发现并没有什么卵用,然后就不停地东改改西凑凑,然后最终还是T掉了,然后学长突然表示,可以把hash改成自然溢出试试,然后,就过了,当时我就表示,如果以后不卡自然溢出,我一定死都不用qnq。本体就是利用平衡树来维护当前的字符串,然后直接进行对以xy开头的两个字符串,二分长度,比对当前的hash值是否相同,每一次split相应的区间就可以了,所以询问的操作应该是二分的操作套上平衡树的操作,应该是log2n所以说,本题的最坏的复杂度应该为nlog2n(询问操作),其余操作均可以logn实现,(不过本题用treap写的确速度比较慢······)

Source

#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <ctime>

inline char read() {
	static const int IN_LEN = 1048576;
	static char buf[IN_LEN], *s, *t;
	if (s == t) {
		t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
		if (s == t) return -1;
	}
	return *s++;
}

/*
template<class T>
inline bool R(T &x) {
	static char c;
	static bool iosig;
	for (c = read(), iosig = false; !isdigit(c); c = read()) {
		if (c == -1) return false;
		if (c == '-') iosig = true;
	}
	for (x = 0; isdigit(c); c = read())
		x = (x << 3) + (x << 1) + (c ^ '0');
	if (iosig) x = -x;
	return true;
}
//*/

const int OUT_LEN = 1048576;
char obuf[OUT_LEN], *oh = obuf;

inline void writechar(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}

template<class T>
inline void W(T x) {
	static int buf[30], cnt;
	if (!x) writechar(48);
	else {
		if (x < 0) writechar('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) writechar(buf[cnt--]);
	}
}

inline void flush() { fwrite(obuf, 1, oh - obuf, stdout); }

///*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
		if (c == '-') iosig = true;
	for (x = 0; isdigit(c); c = getchar())
		x = (x << 3) + (x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

const int h = 31;
const int mod = 9875321;
const int MAXN = 150000 + 10;
char s[MAXN];
unsigned int hash_pow[MAXN];

inline int get_rand() {
    static int rand_seed = 495;
    rand_seed += rand_seed << 1 | 1;
    return rand_seed;
}

struct node *null;
struct node {
	node *lc, *rc;
	int size, rank;
	unsigned int hash;
	char key;
	
	node() {}
	node(char key) : lc(null), rc(null), size(1), rank(get_rand()),
					 hash(key - 'a' + 1), key(key) {}
	
	inline void push_down() {}
	inline void maintain() {
		size = lc->size + 1 + rc->size;
		hash = lc->hash + (unsigned int)(key - 'a' + 1) * hash_pow[lc->size]
			 + (unsigned int)rc->hash * hash_pow[lc->size + 1];
	}
} data[MAXN], *rt;

int top;

struct Pair {
	node* first;
	node* second;
	Pair(node * first, node * second) : first(first), second(second) {}
	Pair() {}
};

inline node* new_node(char key) {
	node *u = &data[top++];
	return *u = node(key), u;
}

inline Pair split(node *u, int k) {
	if (u == null) return Pair(null, null);
	Pair t;
	u->push_down();
	if (k <= u->lc->size) t = split(u->lc, k), u->lc = t.second, t.second = u;
	else t = split(u->rc, k - u->lc->size - 1), u->rc = t.first, t.first = u;
	return u->maintain(), t;
}


inline node* merge(node *u, node *v) {
	if (u == null) return v;
	if (v == null) return u;
	if (u->rank < v->rank) {
		u->push_down(), u->rc = merge(u->rc, v);
		return u->maintain(), u;
	} else {
		v->push_down(), v->lc = merge(u, v->lc);
		return v->maintain(), v;
	}
}

inline node* build(char *a, int n) {
	static node *stack[MAXN], *u, *pre;
	int top = 0;
	for (int i = 0; i < n; ++i) {
		u = new_node(a[i]), pre = null;
		while (top && stack[top]->rank > u->rank)
			stack[top]->maintain(), pre = stack[top], stack[top--] = null;
		if (top) stack[top]->rc = u;
		u->lc = pre, stack[++top] = u;
	}
	while (top) stack[top--]->maintain();
	return stack[1];
}

inline void insert(int pos, char c) {
	node *nr = new_node(c);
	Pair t = split(rt, pos);
	rt = merge(t.first, merge(nr, t.second));
}

inline void change(int pos, char c) {
	Pair t1 = split(rt, pos - 1), t2 = split(t1.second, 1);
	t2.first->key = c, t2.first->hash = c - 'a' + 1;
	rt = merge(t1.first, merge(t2.first, t2.second));
}

inline int query_hash(int pos, int length) {
	Pair t1 = split(rt, pos - 1), t2 = split(t1.second, length);
	int hash = t2.first->hash;
	return rt = merge(t1.first, merge(t2.first, t2.second)), hash;
}

inline int query_lcp(int x, int y) {
	int r = std::min(rt->size - x, rt->size - y) + 2, l = 0;
	while (l + 1 < r) {
		int mid = l + r >> 1;
		if (query_hash(x, mid) == query_hash(y, mid)) l = mid;
		else r = mid;
	}
	return l;
}

inline void pre() {
	hash_pow[0] = 1;
	for (int i = 1; i < MAXN; ++i) 
		hash_pow[i] = hash_pow[i - 1] * h;
}

char s_q[3], s_key[2];
int len, q, x, y, pos;
inline void solve() {
	scanf("%s", s), len = strlen(s);
	null = new_node(0), null->size = null->hash = 0, rt = build(s, len);
	R(q);
	while (q--) {
		scanf("%s", s_q);
		switch (s_q[0]) {
			case 'Q' :
				R(x), R(y), W(query_lcp(x, y)), writechar('\n');
				break;
			case 'R' :
				R(pos), scanf("%s", s_key), change(pos, s_key[0]);
				break;
			case 'I' :
				R(pos), scanf("%s", s_key), insert(pos, s_key[0]);
				break; 
		}
	}
}

int main() {
	pre();
	solve();
	flush();
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【问题描述】 人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。 火星人用一种非常简单的方式来表示数字——掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为1,2,3……。火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。 一个火星人用一个人类的手演示了如何用手指计数。如果把五根手指——拇指、食指、中指、无名指和小指分别编号为1,2,3,4和5,当它们按正常顺序排列时,形成了5位数12345,当你交换无名指和小指的位置时,会形成5位数12354,当你把五个手指的顺序完全颠倒时,会形成54321,在所有能够形成的120个5位数中,12345最小,它表示1;12354第二小,它表示2;54321最大,它表示120。下表展示了只有3根手指时能够形成的6个3位数和它们代表的数字: 三进制数 123 132 213 231 312 321 代表的数字 1 2 3 4 5 6 现在你有幸成为了第一个和火星人交流的地球人。一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。你的任务是,把火星人用手指表示的数与科学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。输入数据保证这个结果不会超出火星人手指能表示的范围。 【输入文件】 输入文件martian.in包括三行,第一行有一个正整数N,表示火星人手指的数目(1 <= N <= 10000)。第二行是一个正整数M,表示要加上去的小整数(1 <= M <= 100)。下一行是1到N这N个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。 【输出文件】 输出文件martian.out只有一行,这一行含有N个整数,表示改变后的火星人手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。 【样例输入】 5 3 1 2 3 4 5 【样例输出】 1 2 4 5 3 用VC环境写的C语言程序,有详尽注释,一看即懂
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值