bzoj 3682: Phorni 后缀平衡树+线段树
来源:互联网 发布:二维动画制作软件 编辑:程序博客网 时间:2024/06/05 18:57
题意
Phorni 是一个音之妖精,喜欢在你的打字机上跳舞。
一天,阳光映射到刚刚淋浴过小雨的城市上时,Phorni 用魔法分裂出了许多个幻影,从 1 到 n 编号。
她的每一个幻影都站在打出的字符串的一个位置上,多个幻影可以站在同一个位置上。
每一个幻影代表的字符串即为从它站立位置开始的后缀,注意站立位置是从右往左数的。
让我们形式化地描述一下,若第 i 个幻影站在 Pi 上,那么它所代表的字符串就是 S[L-Pi+1…L],其中 L 是字符串 S 的长度。
每一次,她会选一段编号区间 [l..r],而编号在这个区间中的幻影中,字典序最小的一个将跳一支舞,若有多个幻影字典序相同,选编号最小的。
当然由于 Phorni 还会在打字机上跳动,所以有时字符串的前面会加入一个字符。
当然这个打字机是带加密功能的。
字典序的比较:
将两个字符串逐位比较,长度不足的向后补 0 ( 0 小于任何字符) 。直到比出大小或判定相等。
比如 “pho” > “ph” , “pb” > “pab” 。
下标从 1 开始,保证涉及到的所有字符都为小写字母。
对于 100% 的数据, 1 ≤ n ≤ 500000, 1 ≤ m ≤ 800000, 1 ≤ Pi ≤ len ≤ 100000。
若 type = 0,保证 I 操作中 0 ≤ c ≤ 25;否则 0 ≤ c xor lastans ≤ 25 。
C 操作中 1 ≤ x ≤ n, 1 ≤ pos ≤ 当前字符串长度。
Q 操作中 1 ≤ l, r ≤ n,I 操作数量约占总操作数量的 1/5,C,Q 操作数量约各占总操作数量的 2/5。
分析
如果离线的话就可以用sa或sam,但现在是强制在线,就只能用后缀平衡树。
具体的后缀平衡树可以看clj13年的论文。
这题的话,相当于每次加入一个后缀,然后我们将这个后缀插入到后缀平衡树中,找到其对应位置。
对于每个后缀我们都可以通过其在平衡树中的位置确定其rank,这个rank可以通过给每个节点赋一个区间来维护。每次treap旋转的时候暴力修改rank即可。因为据说treap的期望旋转次数非常的小,但我并不会证。当然如果觉得不保险的话写替罪羊树也是资瓷的。
对于两个后缀的比较我们可以通过比较两个后缀的首字母和其下一个位置对应后缀的rank来实现。
这题的话,treap不旋转比旋转还要快。。。显然出题人的数据是随机的。
但为了不被hack我还是加了旋转上去。
代码
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=1000005;const LL inf=(LL)1<<61;int n,m,len,ty,s[N],rt,mn[N*5],p[N];LL rank[N];char str[N];struct tree{int l,r,k;}t[N];int read(){ int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}bool cmp(int x,int y){ return s[x]<s[y]||s[x]==s[y]&&rank[x-1]<=rank[y-1];}void rebuild(int d,LL l,LL r){ LL mid=(l+r)/2; rank[d]=mid; if (t[d].l) rebuild(t[d].l,l,mid); if (t[d].r) rebuild(t[d].r,mid+1,r);}void rttl(int &x,LL l,LL r){ int y=t[x].r; t[x].r=t[y].l; t[y].l=x; x=y; rebuild(y,l,r);}void rttr(int &x,LL l,LL r){ int y=t[x].l; t[x].l=t[y].r; t[y].r=x; x=y; rebuild(y,l,r);}void ins(int &d,LL l,LL r,int x){ LL mid=(l+r)/2; if (!d) {d=x;rank[d]=mid;t[d].k=rand()*rand();return;} if (cmp(x,d)) { ins(t[d].l,l,mid,x); if (t[t[d].l].k<t[d].k) rttr(d,l,r); } else { ins(t[d].r,mid,r,x); if (t[t[d].r].k<t[d].k) rttl(d,l,r); }}void modify(int d,int l,int r,int x){ if (l==r) {mn[d]=l;return;} int mid=(l+r)/2; if (x<=mid) modify(d*2,l,mid,x); else modify(d*2+1,mid+1,r,x); mn[d]=cmp(p[mn[d*2]],p[mn[d*2+1]])?mn[d*2]:mn[d*2+1];}int query(int d,int l,int r,int x,int y){ if (x>y) return -1; if (l==x&&r==y) return mn[d]; int mid=(l+r)/2; int u=query(d*2,l,mid,x,min(y,mid)),v=query(d*2+1,mid+1,r,max(x,mid+1),y); return v==-1||u>-1&&cmp(p[u],p[v])?u:v;}int main(){ n=read();m=read();len=read();ty=read(); scanf("%s",str+1); reverse(str+1,str+len+1);s[0]=-1; for (int i=1;i<=len;i++) s[i]=str[i]-'a',ins(rt,0,inf,i); for (int i=1;i<=n;i++) p[i]=read(),modify(1,1,n,i); int ans=0; while (m--) { char ch[2];scanf("%s",ch); if (ch[0]=='I') { int c=read()^(ty*ans);s[++len]=c; ins(rt,0,inf,len); } else if (ch[0]=='C') { int x=read(),pos=read(); p[x]=pos; modify(1,1,n,x); } else { int l=read(),r=read(); printf("%d\n",ans=query(1,1,n,l,r)); } } return 0;}
- bzoj 3682: Phorni 后缀平衡树+线段树
- 后缀平衡树 bzoj3682 Phorni
- BZOJ 3682 Phorni
- 【线段树套平衡树】BZOJ 3196
- BZOJ 3196 线段树套平衡树
- 后缀平衡树
- 后缀平衡树
- BZOJ 3196 二逼平衡树 线段树+treap
- [线段树套平衡树] BZOJ 2883 gss2加强版
- [BZOJ 1058][ZJOI 2007]报表统计 平衡树+线段树
- bzoj 2534: Uva10829L-gap字符串 后缀数组+线段树合并
- bzoj 1396: 识别子串 (后缀自动机+线段树)
- BZOJ 3196: Tyvj 1730 二逼平衡树|线段树套平衡树
- UOJ35 后缀排序[后缀平衡树]
- 后缀平衡树学习笔记
- [bzoj2555][后缀平衡树]SubString
- [后缀平衡树][JZOJ4384]hashit
- [后缀自动机 线段树] BZOJ 1396 识别子串 & BZOJ 2865 字符串识别
- 24点算法
- 持续集成之最佳实践
- Java基础(一):Java面向对象、面向对象封装、抽象类、接口、static、final
- Mac下的SVN客户端
- left join 和left outer join
- bzoj 3682: Phorni 后缀平衡树+线段树
- idea快捷键
- 汇编写启动代码之关看门狗
- [读书笔记]从零开始学C++程序设计
- 排序中慢的永远是一次只比较相邻元素
- maven反向生产对应的实体类
- MYSQL中LIMIT用法
- Wannafly挑战赛5 B-购物
- Android Studio 配置:自定义代码注释.md