[Noi2016十连测第二场]幻想(后缀平衡树)

10 篇文章 0 订阅
7 篇文章 0 订阅

题意:给一个字符串,三种操作,末尾插入一个字符,弹出末尾的字符,查询区间内一个给定字符串出现了多少次。

卡常神题。。500万的数据规模什么鬼。。

假如没有区间限制,就是后缀平衡树的裸题。查询一个字符串s出现了多少次,就是在后缀平衡树上找出<s#的数量减去<s的数量,其中#是一个无穷大的字符,复杂度|s|logn。有区间限制的话写成平衡树套vector即可。。因为后缀平衡树用的是重量平衡树,所以是可以套东西的,重构的时候把vector归并了即可。注意归并之前vector需要resize!!!这个导致的RE使得我不明所以地调了一个多小时。。

由于替罪羊的删除不是很方便,这里可以用treap,删除一个节点的时候直接重构这个节点所在的子树。以前重构操作都是直接写在rotate里面的,用起来非常方便。这里为了常数问题,可以像替罪羊那样记录一个最高的需要重构的点重构即可。

考试的时候有人用分块过了的,就是将询问串分成比格和思茅。以后考试的时候如果不敢写正解的话可以试试这种分块来骗分。

#include<iostream>
#include<algorithm>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<vector>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define ALL(x) (x).begin(), (x).end()
#define Exist(x,y) ((x) ? (x) : (y))
#define pb push_back
#define LL long long
using namespace std;
const int MAXN = 500005;
const LL INF = 1ll<<61;

inline int ran() {
	static int a=5116213;
	return a=(a*1237+521)&0x7fffffff;
}

inline int get(char*s)
{
	int len = 0, c;
	do c=getchar(); while(!isalpha(c));
	do s[len++]=c, c=getchar(); while(isalpha(c));
	return s[len] = '\0', len;
}
inline int get(int&r)
{
	char c; r=0;
	do c=getchar(); while(c<'0'||c>'9');
	do r=r*10+c-'0',c=getchar(); while(c>='0'&&c<='9');
}
inline int cid(char x) { return x<='Z'?x-'A'+26:x-'a'; }
inline char wrd(int x) { return x<26?x+'a':x-26+'A'; }

char str[MAXN], s[MAXN*10];
int Q, lens, ans;
#define lch(x) (x->ch[0])
#define rch(x) (x->ch[1])
#define nodepos(x) ((x)-nds)
#define siz(x) (x?x->sz:0)
struct Node {
	Node *ch[2], *nxt;
	int fix, c;
	LL key;
	vector<int> vec;
} nds[MAXN], *ncnt = nds, *rt;

inline int calc(vector<int>&v, int l, int r)
{
	return upper_bound(ALL(v), r)-lower_bound(ALL(v), l);
}
inline void rotate(Node*&x, int d)
{
	Node*y = x->ch[!d];
	x->ch[!d] = y->ch[d];
	y->ch[d] = x;
	x = y;
}
inline int cmp(Node*x, Node*y)
{
	if (x->c^y->c) return x->c < y->c;
	return x->nxt->key < y->nxt->key;
}

inline void upvec(Node*x)
{
	x->vec.clear();
	if (lch(x)&&rch(x))
	{
		x->vec.resize(lch(x)->vec.size()+rch(x)->vec.size());
		merge(ALL(lch(x)->vec), ALL(rch(x)->vec), x->vec.begin());
	}
	else if (lch(x)&&!rch(x)) x->vec = lch(x)->vec;
	else if (rch(x)&&!lch(x)) x->vec = rch(x)->vec;
	int t = nodepos(x);
	x->vec.insert(upper_bound(ALL(x->vec), t), t);
}

void rebuild(Node*x, LL l, LL r)
{
	if (!x) return;
	LL mid = l+r>>1;
	x->key = l+r;
	rebuild(lch(x), l, mid);
	rebuild(rch(x), mid, r);
	upvec(x);
}

int rotflag;
void ins(Node*&x, LL l, LL r)
{
	if (!x) { (x=ncnt)->key = l+r; return; }
	int d = cmp(x, ncnt);  //d=0表示插入节点比当前节点小
	LL mid = l+r>>1;
	if (!d) ins(lch(x), l, mid);
	else ins(rch(x), mid, r);
	if (x->ch[d]->fix > x->fix)
		rotate(x, !d);
	else
	{
		if (rotflag)
		{
			if (!d) rebuild(lch(x), l, mid);
			else rebuild(rch(x), mid, r);
			rotflag = 0;
		}
		x->vec.pb(nodepos(ncnt));
	}
	if (x==rt&&rotflag)
		rebuild(x, l, r);
}

void del(Node*&x, LL l, LL r)
{
	LL mid = l+r>>1;
	if (x==ncnt)
	{
		bool firstmeet = rotflag;
		rotflag = 0;
		if (!lch(x) || !rch(x))
		{
			x = Exist(lch(x), rch(x));
			if (!x) return;
		}
		else {
			if (lch(x)->fix>rch(x)->fix)
				rotate(x, 1), del(rch(x), mid, r);
			else
				rotate(x, 0), del(lch(x), l, mid);
		}
		if (firstmeet) rebuild(x, l, r);
	}
	else
	{
		x->vec.pop_back();
		if (ncnt->key < x->key) del(lch(x), l, mid);
		else del(rch(x), mid, r);
	}
}

inline void extend(int c)
{
	str[++lens] = c;
	(++ncnt)->c=c;
	ncnt->fix=ran(), ncnt->vec.clear();
	lch(ncnt)=rch(ncnt)=0x0, ncnt->nxt=ncnt-1;
	rotflag = 1, ins(rt, 1, INF);
}

inline void popchar()
{
	rotflag = 1, del(rt, 1, INF);
	ncnt --, lens --;
}

int revlcp(Node*x, char*s, int len)
{
	int p = nodepos(x), l = 0;
	while (l<p&&l<len&&str[p-l]==s[len-l]) l++;
	return l;
}

int calcless(Node*x, char*s, int len, int l, int r) //查询x树中比s小的数量
{
	if (l>r||!x) return 0;
	int sm = revlcp(x, s, len), d, p=nodepos(x); //d表示x是否比s小
	if (sm==len) d = p<len;
	else d = s[len-sm]>str[p-sm];
	int flag = p>=l&&p<=r&&d;
	if (!d) return calcless(lch(x), s, len, l, r);
	int lef = lch(x)?calc(lch(x)->vec, l, r):0;
	return lef + flag + calcless(rch(x), s, len, l, r);
}

int quary(int l, int r, char*s, int len)
{
	int L=l+len-1, R=r;
	int sum1 = calcless(rt, s, len, L, R);
	len++;
	erp(i, len, 2) s[i] = s[i-1];
	s[1] = 'z'+1;
	int sum2 = calcless(rt, s, len, L, R);
	return sum2 - sum1;
}

void decode(char*s, int len)
{
	rep(i, 1, len) s[i] = wrd((cid(s[i])+ans)%52);
}

int main()
{
	int len = get(s+1);
	rep(i,1,len) extend(s[i]);
	get(Q);
	for (int op, l, r; Q--; )
	{
		get(op);
		if (op==1)
		{
			get(s+1);
			decode(s,1);
			extend(s[1]);
		}
		else if (op==2) popchar();
		else {
			get(l), get(r);
			l^=ans, r^=ans;
			len = get(s+1);
			decode(s,len);
			ans = quary(l, r, s, len);
			printf("%d\n", ans);
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值