FHQ_treap究极完全板子

究极FHQ_treap非指针版板子


例题

匹配

题目描述

给定一个仅含小写字母的字符串 S[0..n-1],对于一个询问 (p, q, len),我们想知道它的两个子串 S[p..p+len-1]、S[q..q+len-1] 是否相同。更多地,我们希望在对串 S 完成一些操作之后还能高效地得到这个结果。 我们具体要维护以下几个操作(其中 L 为操作之前的串长):
● 1 p c:在下标 p 之前插入一个小写字母 c,0<=p<=L,p=0 意味在头插入,p=L意味在尾插入;
● 2 p: 删除 S[p],其中 0<=p < L;
● 3 p q: 将 S[p..q] 翻转为 S[q..p],例如 batta 经过 “3 1 3” 之后为 bttaa;
● 4 p q len: 表示一次询问操作,询问 S[p..p+len-1] 与 S[q..q+len-1] 是否相同,其中 0<=p<=p+len-1

输入格式

第 1 行为两个整数 n, m,分别表示字符串的初始长度和操作次数;
第 2 行为一个长度为 n 的字符串;
接下来 m 行,每行为一个操作。

输出格式

仅一行一个整数,表示询问相同的次数。

样例输入

4 4
aacb
2 2   // aab
1 2 b   // aabb
3 1 2   // abab
4 0 2 2   // ab == ab

样例输出

1

数据范围与约定

对于前 20%的数据满足 n,m≤1000;
对于前 70%的数据满足仅含有 3、4 两种操作;
对于 100%的数据满足 1≤n, m≤200000。



解题思路

操作1 2 就是基本的加点和删点
操作3 参考洛谷P3391文艺平衡树区间翻转板题
对于操作4,给每个节点对应的序列维护hash来比较
值得注意的是
打上lazy以后一定要处理这个节点的值
因为在update操作中
单独只处理当前根的hash值是不够的,会调用到子节点的hash值


code:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;

inline int read() {
	char ch = getchar();
	int res = 0;
	while (ch < '0' || ch > '9') ch = getchar();
	while (ch >= '0' && ch <= '9') {
		res = (res << 1) + (res << 3) + (ch ^ 48);
		ch = getchar();
	}
	return res;
}

const int N = 400005;
mt19937 rd(20051207);  //LUCKY NUMBER!!
ull pw[N], pw2[N], base = 2333, base2 = 20051207;
//const ull md1 = 1e18 + 7, md2 = 1e18 + 9; 


struct FHQ_treap {
	int ls[N], rs[N], siz[N], rank[N];
	int val[N], lazy[N];
	int cnt;
	ull hs[N][2], hs2[N][2];
	void add_lazy(int rt) {
		if (rt) {
			lazy[rt] ^= 1;
			swap(ls[rt], rs[rt]);
			swap(hs[rt][0], hs[rt][1]);
			swap(hs2[rt][0], hs2[rt][1]);
		}
	}
	void push_down(int rt) {
		if (lazy[rt]) {
			add_lazy(ls[rt]);
			add_lazy(rs[rt]);
			lazy[rt] = 0;
		}
		return ;
	}
	/*
	非常重要的批注:
		打上lazy以后一定要处理这个节点的值
		因为在update操作中
		单独只处理当前根的hash值是不够的,会调用到子节点的值
		
		总而言之标记不是指这个点需要处理(所以打上标记的时候就要把相关的值都算好) 
		而是指这个点的子节点需要处理 
	*/
	void update(int rt) {//这题自然溢出不会挂
		siz[rt] = siz[ls[rt]] + siz[rs[rt]] + 1;
		int l = ls[rt], r = rs[rt];
		hs[rt][0] = (  hs[l][0] * pw[siz[r] + 1] + val[rt] * pw[siz[r]] + hs[r][0]  ); // % md1;
		hs[rt][1] = (  hs[l][1] + val[rt] * pw[siz[l]] + hs[r][1] * pw[siz[l] + 1]  ); // % md1;
		hs2[rt][0] = (  hs2[l][0] * pw2[siz[r] + 1] + val[rt] * pw2[siz[r]] + hs2[r][0]  ); // % md2;
		hs2[rt][1] = (  hs2[l][1] + val[rt] * pw2[siz[l]] + hs2[r][1] * pw2[siz[l] + 1]  ); // % md2;
		
	}
	void split_val(int rt, int v, int &l, int &r) {//按值大小分裂
		if (!rt) return l = r = 0, void();
		push_down(rt);
		if (val[rt] <= v) split_val(rs[l = rt], v, rs[rt], r);
		if (val[rt] > v) split_val(ls[r = rt], v, l, ls[rt]);
		update(rt);
	}
	void split_siz(int rt, int k, int &l, int &r) {//按中序分成左边k个点, 剩下的在右边
		if (!rt) return l = r = 0, void();
		push_down(rt);
		if (siz[ls[rt]] < k)
			split_siz(rs[l = rt], k - siz[ls[rt]] - 1, rs[rt], r);
		else split_siz(ls[r = rt], k, l, ls[rt]);
		update(rt);
	} 
	int merge(int l, int r) {
		if (!l || !r) return l | r;
		int rt;
		if (rank[l] <= rank[r]) push_down(l), rs[rt = l] = merge(rs[l], r);
		if (rank[l] > rank[r]) push_down(r), ls[rt = r] = merge(l, ls[r]);
		update(rt);
		return rt;
	}
	int newnode(int v = 0) {
		++cnt;
		ls[cnt] = rs[cnt] = 0;
		siz[cnt] = 1;
		val[cnt] = v;
		hs[cnt][0] = hs[cnt][1] = v;
		hs2[cnt][0] = hs2[cnt][1] = v;
		rank[cnt] = rd();
		return cnt;
	}
	void insert_val(int &rt, int v) {
		int x, y;
		split_val(rt, v, x, y);
		rt = merge(merge(x, newnode(v)), y);
	}
	void erase_val(int &rt, int v) {
		int x, y, z;
		split_val(rt, v - 1, x, y);
		split_val(y, v, y, z);
		y = merge(ls[y], rs[y]);
		rt = merge(merge(x, y), z);
	}
	void insert_siz(int &rt, int k, int v = 0) {
		int x, y;
		split_siz(rt, k, x, y);
		rt = merge(merge(x, newnode(v)), y);
	}
	void erase_siz(int &rt, int k) {//删去中序的第k个 
		int x, y, z;
		split_siz(rt, k - 1, x, y);
		split_siz(y, 1, y, z);
		//y = merge(ls[y], rs[y]);
		rt = merge(x, z);
	}
	void print_mid(int rt) {
		if (!rt) return ;
		push_down(rt);
		print_mid(ls[rt]);
//		printf("%c", char(val[rt] + 'a'));
		print_mid(rs[rt]);
	}
	void reverse(int &rt, int l, int r) {
		int x, y, z;
		split_siz(rt, l - 1, x, y);
		split_siz(y, r - l + 1, y, z);
		add_lazy(y);
		push_down(y);
		rt = merge(merge(x, y), z);
		return ;
	}
	bool cmp(int &rt, int l, int r, int len) {
		int x, y, z;
		split_siz(rt, l - 1, x, y);
		split_siz(y, len, y, z);
//		print_mid(y); //cout<<endl;
		ull tag01 = hs[y][0], tag02 = hs[y][1], tag03 = hs2[y][0], tag04 = hs2[y][1];
		rt = merge(merge(x, y), z);
		
		split_siz(rt, r - 1, x, y);
		split_siz(y, len, y, z);
//		print_mid(y); //cout<<endl;
		ull tag11 = hs[y][0], tag12 = hs[y][1], tag13 = hs2[y][0], tag14 = hs2[y][1];
		rt = merge(merge(x, y), z);
		
		return tag01 == tag11 /*&& tag02 == tag12*/ && tag03 == tag13 /*&& tag04 == tag14*/;
	}
}T;

void init() {
	pw[0] = pw2[0] = 1;
	for (int i = 1; i <= N - 2; i++) {
		pw[i] = pw[i - 1] * base; // % md1;
		pw2[i] = pw2[i - 1] * base2; // % md2;
	}
}

int n, m;
char s[N];

signed main() {
//	freopen("match.in", "r", stdin);
//	freopen("match.out", "w", stdout);
	init();
	n = read(); m = read();
	scanf("%s", s + 1);
	int rt = 0, len = strlen(s + 1);
	for (int i = 1; i <= len; i++) {
		T.insert_siz(rt, i - 1, s[i] - 'a');
	}
	
//	T.print_mid(rt); cout<<endl; 
	int ty, p, q, ans = 0; char ch;
	while (m--) {
		ty = read(); p = read();
		if (ty == 1) {
			ch = getchar();
			T.insert_siz(rt, p, ch - 'a'); 
		}
		else if (ty == 2) T.erase_siz(rt, p + 1); 
		else if (ty == 3) {
			q = read();
			T.reverse(rt, p + 1, q + 1); 
		}
		else if (ty == 4)  {
			q = read(); len = read();
			if (T.cmp(rt, p + 1, q + 1, len)) ans++;
//			printf("ans = %d\n", ans);
		}
//		T.print_mid(rt); //cout<<endl; 
	}
	printf("%d", ans);
}
/*
*/

还有string暴力版
(谨记我痛苦的对拍时光)

#include<bits/stdc++.h>
#define reg register int
using namespace std;
const int N = 2e5 + 5;
int n, m, ans;
string s;

inline bool cmp(int p, int q, int len) {
	for(reg i = 0; i < len; i++) if(s[p + i] != s[q + i]) return false;
	return true;
}

int main() {
//	freopen("match.in","r",stdin);
//	freopen("match.out","w",stdout);
	int opt, p, q, len;
	char c;
	scanf("%d %d", &n, &m);
	cin>>s;
	while(m--) {
		scanf("%d", &opt);
		switch(opt) {
			case 1: {
				scanf("%d %c\n", &p, &c);
				s.insert(p, 1, c);
				break;
			}
			case 2: {
				scanf("%d", &p);
				s.erase(p, 1);
				break;
			}
			case 3: {
				scanf("%d %d", &p, &q);
				reverse(s.begin() + p,s.begin() + q + 1);
				break;
			}
			case 4: {
				scanf("%d %d %d", &p, &q, &len);
				if(cmp(p,q,len)) ans++;
				break;
			}
		}
	}
	printf("%d\n", ans);
	return 0;
}

posted @ 2022-05-24 15:47  Aiza  阅读(90)  评论(0编辑  收藏  举报