平衡树基础
treap可旋转 搜索,插入和删除操作的期望时间复杂度为Olog(n) 这是一道模板题。 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入 数; 删除 数(若有多个相同的数,因只删除一个); 查询 数的排名(若有多个相同的数,因输出最小的排名); 查询排名为 的数; 求 的前趋(前趋定义为小于 ,且最大的数); 求 的后继(后继定义为大于 ,且最小的数)。 输入格式 第一行为 ,表示操作的个数,下面 行每行有两个数 和 , 表示操作的序号()。 输出格式 对于操作 3、4、5、6 每行输出一个数,表示对应答案。 const int SIZE=100010; struct Treap { int l,r; int val,dat; int cnt,size;//cnt是副本数,siz子树中所有节点副本数之和 +自己的副本数 } a[SIZE]; int tot,root,n,INF=0x7fffffff; int New(int val) { a[++tot].val=val; a[tot].dat=rand(); a[tot].size=a[tot].cnt=1; return tot; } void Update(int p) { a[p].size=a[a[p].l].size+a[a[p].r].size+a[p].cnt; } void Build() { New(-INF); New(INF); a[1].r=2; root=1; Update(root);//这鬼东西有什么用? } int GetRankByVal(int p,int val)//直接统计累加所有比val小的节点数量 { if(p==0)return 0; if(val==a[p].val)return a[a[p].l].size+1;//??加上部分比这个值小的节点数+自己,那把这个+1去掉后面是不是就不用-1了?待会试一试 if(val<a[p].val)return GetRankByVal(a[p].l,val); return GetRankByVal(a[p].r,val)+a[a[p].l].size+a[p].cnt; } //int GetValByRank(int p,int rank)//这里吧rank-1,吧后面改成不严格取等对不对?? //{ // if(p==0)return INF; // if(a[a[p].l].size>=rank)return GetValByRank(a[p].l,rank); // if(a[a[p].l].size+a[p].cnt>=rank)return a[p].val;//不在左二子范围内,只能是这个节点的 // return GetValByRank(a[p].r,rank-a[a[p].l].size-a[p].cnt); //} int GetValByRank(int p,int rank) { if(p==0)return INF; if(a[a[p].l].size>=rank)return GetValByRank(a[p].l,rank); if(a[a[p].l].size+a[p].cnt>=rank)return a[p].val; return GetValByRank(a[p].r,rank-a[a[p].l].size-a[p].cnt); } void zig(int &p) { int q=a[p].l; a[p].l=a[q].r,a[q].r=p,p=q; Update(a[p].r);Update(p); } void zag(int &p) { int q=a[p].r; a[p].r=a[q].l,a[q].l=p,p=q; Update(a[p].l);Update(p); } void Insert(int &p,int val) { if(p==0) { p=New(val); return; } if(val==a[p].val) { a[p].cnt++,Update(p); return; } if(val<a[p].val) { Insert(a[p].l,val); if(a[p].dat<a[a[p].l].dat)zig(p);//?? } else { Insert(a[p].r,val); if(a[p].dat>a[a[p].r].dat)zag(p); } Update(p); } int GetPre(int val) { int ans=1,p=root; while(p) { if(a[p].val==val) { if(a[p].l) { p=a[p].l; while(a[p].r>0)p=a[p].r; ans=p; } break; } if(a[p].val<val&&a[p].val>a[ans].val)ans=p; p=(a[p].val>val)?(a[p].l):a[p].r; } return a[ans].val; } int GetNext(int val) { int ans=2,p=root;//INF while(p) { if(a[p].val==val) { if(a[p].r>0) { p=a[p].r; while(a[p].l>0)p=a[p].l; ans=p; } break; } if(a[p].val>val&&a[p].val<a[ans].val)ans=p; p=(a[p].val>val)?(a[p].l):(a[p].r); } return a[ans].val; } void Remove(int&p,int val) { if(p==0)return;//没有什么可以删除的 if(val==a[p].val) { if(a[p].cnt>1) { a[p].cnt--; Update(p); return; } if(a[p].l||a[p].r) { if((a[p].r==0)||(a[p].dat<a[a[p].l].dat)) { zig(p);Remove(a[p].r,val); } else { zag(p);Remove(a[p].l,val); } Update(p);//旋转之后必更新 } else p=0; return; } val<a[p].val?(Remove(a[p].l,val)):(Remove(a[p].r,val)); Update(p); } int main() { srand(time(0)); Build(); cin>>n; while(n--) { int opt,x; opt=re(),x=re(); if(opt==1) { Insert(root,x); } else if(opt==2) { Remove(root,x); } else if(opt==3) { chu("%d\n",GetRankByVal(root,x)-1);//?按大小的排名 } else if(opt==4) { chu("%d\n",GetValByRank(root,x+1));//查询排名为x+1的数据和x什么关系? } else if(opt==5) { chu("%d\n",GetPre(x)); } else if(opt==6) { chu("%d\n",GetNext(x)); } } return 0; }
splay:更加灵活的数据结构
支持:1.区间翻转
2.其他所有treap支持的简单操作(添加,删除,前驱,后继,排名)
Splay依靠的并不是完全的平衡,根据90-10法则,90%的询问都发生在10%的数据上。
Splay的原理就是:找到询问频率最高的点,把它旋转到根节点,以此在下面的询问中提高效率。
我们认为,我正在访问的点就是询问频率最高的点。
让x成为树根(y):
1.如果y是x的父亲,向上旋x
2.如果x和x的父亲在树上偏的方向相同(左or右孩子),先让x的父亲向上旋,再旋x
3.else让x连续旋两次
//为什么分情况?可以自行画图看一看
//发现按照上述旋转,每次splay以后,整棵树十分的平衡!(接近于完全二叉树)
//如果不分情况,直接无脑上旋,则会结构变得比较乱
需要注意的点是:
splay的时候,如果说爷爷已经是goal就不再翻了(翻到0就wa了)
#define lson s[x].son[0] #define rson s[x].son[1] const int INF=100000089,MAXN=1000007; struct SPLAY { int f,sub_size,value,tag; int son[2]; }s[MAXN]; int original[MAXN],root,wz; inline bool which(int x) { return x==s[s[x].f].son[1]; } inline void update(int x) { if(x) { s[x].sub_size=1; if(s[x].son[0])s[x].sub_size+=s[s[x].son[0]].sub_size; if(s[x].son[1])s[x].sub_size+=s[s[x].son[1]].sub_size; } } inline void pushdown(int x) { if(x&&s[x].tag)//x?? { s[x].tag=0; swap(s[x].son[1],s[x].son[0]); s[lson].tag^=1; s[rson].tag^=1; } } inline void rotate(int x) { int fnow=s[x].f,ffnow=s[fnow].f; pushdown(x),pushdown(fnow); bool w=which(x); s[s[x].son[w^1]].f=fnow; s[fnow].son[w]=s[x].son[w^1]; s[x].son[w^1]=fnow; s[fnow].f=x; s[x].f=ffnow; if(ffnow) { s[ffnow].son[s[ffnow].son[1]==fnow]=x; } update(fnow); update(x); } inline void splay(int x,int goal)//目标指的是让x的父亲是goal { //chu("dsfdsfdsfdsfdsf\n"); while(s[x].f!=goal) //只要没到达目标状态 { // chu("qqq\n"); int y=s[x].f,z=s[y].f; if(z!=goal)//如果爷爷已经是目标直接把x旋上去,不能再把goal给 //随便旋转了 { (s[z].son[1]==y)^(s[y].son[1]==x)?rotate(x):rotate(y); } rotate(x); } if(goal==0)root=x;//goal=0就代表着旋转到根节点?? } int build_tree(int l,int r,int fa) { if(l>r)return 0; int mid=(l+r)>>1; int now=wz+1; wz++; s[now].f=fa; s[now].son[0]=build_tree(l,mid-1,now); s[now].son[1]=build_tree(mid+1,r,now); s[now].value=original[mid]; s[now].sub_size=1; update(now); return now; } inline int find(int x){ int now=root; while(1) { //chu("rrr\n"); pushdown(now); if(x<=s[s[now].son[0]].sub_size){ now=s[now].son[0]; // chu("now:%d\n",now); } else { x-=s[s[now].son[0] ].sub_size + 1; //chu("changex:%d\n",x); if(!x)return now; now=s[now].son[1]; } } } inline void reverse(int x,int y) { int l=x-1,r=y+1; l=find(l);r=find(r); //chu("dfsdf\n"); splay(l,0); // chu("dfsdf\n"); splay(r,l); // chu("dfs\n"); int pos=s[root].son[1]; pos=s[pos].son[0]; s[pos].tag^=1; } inline void dfs(int now) { pushdown(now); if(s[now].son[0])dfs(s[now].son[0]); if(s[now].value!=-INF&&s[now].value!=INF) chu("%d ",s[now].value); if(s[now].son[1])dfs(s[now].son[1]); } int main() { // freopen("makeans.txt","r",stdin); // freopen("fire.out","w",stdout); int n,m,x,y; n=re(),m=re(); original[1]=-INF; original[n+2]=INF; _f(i,1,n)original[i+1]=i; root=build_tree(1,n+2,0); _f(i,1,m) { x=re(),y=re(); reverse(x+1,y+1); } dfs(root); return 0; }
3.给出一段数列,要求查找区间范围内的前驱,后继,一个数排名,排名是几的数
修改位置的值
方法:线段树套平衡树
1 #include<bits/stdc++.h> 2 #define rep(i, x, y) for (int i = (x); i <= (y); i ++) 3 #define down(i, x, y) for (int i = (x); i >= (y); i --) 4 #define mid ((l+r)/2) 5 #define lc (o<<1) 6 #define rc (o<<1|1) 7 #define pb push_back 8 #define mp make_pair 9 #define PII pair<int, int> 10 #define F first 11 #define S second 12 #define B begin() 13 #define E end() 14 using namespace std; 15 typedef long long LL; 16 //head 17 18 const int N = 4000010; 19 const int INF = 2147483647; 20 int n, m, tot, ans, MX; 21 int a[N], sz[N], cnt[N], ch[N][2], fa[N], data[N], rt[N]; 22 23 //===================================================================== 24 //平衡树 25 26 inline void splayClear(int x) 27 { 28 fa[x] = ch[x][0] = ch[x][1] = sz[x] = cnt[x] = data[x] = 0; 29 } 30 31 inline void pushup(int x) 32 { 33 sz[x] = (ch[x][0]?sz[ch[x][0]]:0) + (ch[x][1]?sz[ch[x][1]]:0) + cnt[x]; 34 } 35 36 inline void rot(int x) 37 { 38 int y = fa[x], z = fa[y]; bool f = ch[y][1] == x; 39 ch[y][f] = ch[x][f^1]; if (ch[x][f^1]) fa[ch[x][f^1]] = y; 40 fa[x] = z; if (z) ch[z][ch[z][1] == y] = x; 41 fa[y] = x; ch[x][f^1] = y; 42 pushup(y); pushup(x); 43 } 44 45 inline void splay(int i, int x, int top) 46 { 47 while (fa[x] != top){ 48 int y = fa[x], z = fa[y]; 49 if (z != top) rot((ch[z][0] == y) == (ch[y][0] == x) ? y : x); 50 rot(x); 51 } 52 if (!top) rt[i] = x; 53 } 54 55 inline void splayInsert(int i, int v) 56 { 57 int x = rt[i]; 58 if (!rt[i]){ 59 rt[i] = x = ++ tot; 60 data[x] = v; sz[x] = cnt[x] = 1; 61 fa[x] = ch[x][0] = ch[x][1] = 0; 62 return; 63 } int last = 0; 64 while (1){ 65 if (data[x] == v){ cnt[x] ++; pushup(last); break; } 66 last = x; 67 x = ch[x][v > data[x]]; 68 if (!x){ 69 x = ++ tot; data[x] = v; sz[x] = cnt[x] = 1; 70 ch[last][v > data[last]] = x; 71 fa[x] = last; ch[x][0] = ch[x][1] = 0; 72 pushup(last); break; 73 } 74 } 75 splay(i, x, 0); 76 } 77 78 inline int splayRank(int i, int v)//在第i棵splay中求比v小的数的个数 79 { 80 int x = rt[i], ret = 0; 81 while (x){ 82 if (data[x] == v) return ret + ((ch[x][0])?sz[ch[x][0]]:0); 83 if (data[x] < v){ 84 ret += ((ch[x][0])?sz[ch[x][0]]:0) + cnt[x]; 85 x = ch[x][1]; 86 } else x = ch[x][0]; 87 } 88 return ret; 89 } 90 91 inline int splayFind(int i, int v)//在第i棵splay中找到值为v的节点并将它提升到根 92 { 93 int x = rt[i]; 94 while (x){ 95 if (data[x] == v){ splay(i, x, 0); return x; } 96 x = ch[x][v > data[x]]; 97 } 98 } 99 100 inline int splayPre(int i){ int x = ch[rt[i]][0]; while (ch[x][1]) x = ch[x][1]; return x; } 101 inline int splaySuc(int i){ int x = ch[rt[i]][1]; while (ch[x][0]) x = ch[x][0]; return x; } 102 103 inline void splayDelete(int i, int key)//将第i棵splay的值为key的元素删掉 104 { 105 int x = splayFind(i, key); 106 if (cnt[x] > 1){ cnt[x] --; pushup(x); return; } 107 if (!ch[x][0] && !ch[x][1]){ splayClear(rt[i]); rt[i] = 0; return; } 108 if (!ch[x][0]){ 109 int y = ch[x][1]; rt[i] = y; fa[y] = 0; 110 return; 111 } 112 if (!ch[x][1]){ 113 int y = ch[x][0]; rt[i] = y; fa[y] = 0; 114 return; 115 } 116 int p = splayPre(i); int oldrt = rt[i]; 117 splay(i, p, 0); 118 ch[p][1] = ch[oldrt][1]; fa[ch[oldrt][1]] = rt[i]; 119 splayClear(oldrt); 120 pushup(p); 121 } 122 123 inline int splayGetpre(int i, int v) 124 { 125 int x = rt[i]; 126 while (x){ 127 if (data[x] < v){ 128 if (ans < data[x]) ans = data[x]; 129 x = ch[x][1]; 130 } else x = ch[x][0]; 131 } return ans; 132 } 133 134 inline int splayGetsuc(int i, int v) 135 { 136 int x = rt[i]; 137 while (x){ 138 if (data[x] > v){ 139 if (ans > data[x]) ans = data[x]; 140 x = ch[x][0]; 141 } else x = ch[x][1]; 142 } return ans; 143 } 144 145 //===================================================================== 146 //线段树 147 148 inline void segInsert(int o, int l, int r, int x, int w) 149 { 150 splayInsert(o, w); 151 if (l == r) return; 152 if (x <= mid) segInsert(lc, l, mid, x, w); 153 else segInsert(rc, mid+1, r, x, w); 154 } 155 156 //inline void segRank(int o, int l, int r, int x, int y, int v) 157 //{ 158 // if (l == x && r == y){ ans += splayRank(o, v); return; } 159 // if (y <= mid) segRank(lc, l, mid, x, y, v); 160 // else if (x > mid) segRank(rc, mid+1, r, x, y, v); 161 // else segRank(lc, l, mid, x, mid, v), segRank(rc, mid+1, r, mid+1, y, v); 162 //} 163 inline void segRank(int o, int l, int r, int x, int y, int v) 164 { 165 if (x<=l&&r<=y){ ans += splayRank(o, v); return; } 166 if (x <= mid) segRank(lc, l, mid, x, y, v); 167 if (y > mid) segRank(rc, mid+1, r, x, y, v); 168 } 169 170 inline void segChange(int o, int l, int r, int x, int v) 171 { 172 splayDelete(o, a[x]); splayInsert(o, v); 173 if (l == r){ a[x] = v; return; } 174 if (x <= mid) segChange(lc, l, mid, x, v); 175 else segChange(rc, mid+1, r, x, v); 176 } 177 178 179 inline void segPre(int o, int l, int r, int x, int y, int v) 180 { 181 if(x<=l&&r<=y) 182 { 183 ans=max(ans,splayGetpre(o,v));return; 184 } 185 if (x <= mid) segPre(lc, l, mid, x, y, v); 186 if (y > mid) segPre(rc, mid+1, r, x, y, v); 187 } 188 189 inline void segSuc(int o,int l,int r,int x,int y,int v) 190 { 191 if(x<=l&&r<=y) 192 { 193 ans=min(ans,splayGetsuc(o,v));return; 194 } 195 if(x<=mid)segSuc(lc,l,mid,x,y,v); 196 if(y>mid)segSuc(rc,mid+1,r,x,y,v); 197 } 198 199 200 //===================================================================== 201 //第二问 202 203 inline int getKth(int x, int y, int k) 204 { 205 int ll = 1, rr = MX, mm; 206 while (ll < rr){ 207 mm = (ll + rr) / 2; 208 ans = 0; segRank(1, 1, n, x, y, mm); 209 if (ans <=(k-1)) ll = mm+1; 210 else rr = mm ; 211 } 212 return ll-1; 213 } 214 这里的二分很精妙 215 当mm值超过了k-1,mm一定不合法 216 但是我让rr=mm 217 这样在mid二分下,rr会逼近ans,但是永远>ans 218 我让ll去在被勒住的区间不断查找答案,合法时ll=mid+1 219 返回ll-1 220 1 2 2 2 2 4 221 rr在4 的位置,ll是2的话,mid=3, 222 rr跳到3,mid=2,合法,ll=3,跳出来,2就是合法的 223 恰好是不合法值的上一个是合法的, 224 如果这个值不存在,在l的查找下也会筛出存在的 225 2 2 3 10(query:3) 226 2 2 4 10(query:3) 227 当mid=3时ll会跳到4 228 如果是1,直接不合法跳了 229 如果是2,还是合法会继续 230 mid=4 231 如果是1,rr=4 232 如果是2,ll=5 233 反正怎么都对 234 不能勒左边,因为向下取整 235 int main() 236 { 237 scanf("%d%d", &n, &m); 238 rep(i, 1, n){ 239 scanf("%d", &a[i]); 240 segInsert(1, 1, n, i, a[i]); 241 MX = max(MX, a[i]); 242 } 243 while (m --){ 244 int opt, x, y, z; scanf("%d%d%d", &opt, &x, &y); 245 if (opt == 1) scanf("%d", &z), ans = 0, segRank(1, 1, n, x, y, z), printf("%d\n", ans+1); 246 else if (opt == 2) scanf("%d", &z), printf("%d\n", getKth(x, y, z)); 247 else if (opt == 3) segChange(1, 1, n, x, y),MX=max(MX,y);//?? 248 else if (opt == 4) scanf("%d", &z), ans = -INF, segPre(1, 1, n, x, y, z), printf("%d\n", ans); 249 else scanf("%d", &z), ans = INF, segSuc(1, 1, n, x, y, z), printf("%d\n", ans); 250 } 251 return 0; 252 } 253 /* 254 9 6 255 3 2 2 1 9 4 0 1 1 256 2 1 4 3 257 3 4 10 258 2 1 4 3 259 1 2 5 9 260 4 3 9 5 261 5 2 8 5 262 */
4.应用:
平衡树的本质是在维护一段序列相对大小关系(多半是位置)的前提下,在序列中快速加入或者删除个别元素或区间,并查询相关信息
如果求一段序列,LCQ(i,j)(以此为起点的最大公共前缀个数)
这段序列支持单点修改和修改
用平衡树维护位置大小关系,每个节点代表这颗子树代表的子串
hsh[rt]=hsh[lson]+hsh[rt]*p[sizelson]+hsh[rson]*(p[sizelson+1])
正常的加点和改点,同时动态维护hsh
insert:rt find(rtsub) rt根,rtsub右儿子insert(s[rt].son[0])
#include<iostream> #include<cstring> #include<cmath> #include<cstdio> #include<string> #include<cstdlib> #include<ctime> #include<algorithm> #include<iomanip> #include<bitset> #include<map> #include<queue> #include<bitset> #include<deque> #include<vector> #define _f(i,a,b) for(register int i=a;i<=b;++i) #define f_(i,a,b) for(register int i=a;i>=b;--i) #define ull unsigned long long #define chu printf #define inf 0x7f7f7f7f using namespace std; inline int re() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') { f=-1; } ch=getchar(); } while(ch<='9'&&ch>='0') { x=(x<<1)+(x<<3)+(ch^48);ch=getchar(); } return x*f; } const int SIZE=1000000; struct SPLAY { int son[2],fa,size; int w;ull hsh; }s[200000]; int tot,rt; //29 ull p[150004];//100000 char ss[150000+10]; int len; inline void Update(int x) { s[x].size=1; if(s[x].son[0])s[x].size+=s[s[x].son[0]].size; if(s[x].son[1])s[x].size+=s[s[x].son[1]].size; s[x].hsh=s[s[x].son[0]].hsh+s[x].w*p[s[s[x].son[0]].size]+s[s[x].son[1]].hsh*p[s[s[x].son[0]].size+1]; //chu("c=uodate: %d %lld\n",x,s[x].hsh); } inline int Build(int l,int r,int f) { if(l>r)return 0; int mid=(l+r)>>1; s[mid].fa=f;s[mid].w=ss[mid]-'a'+1; s[mid].son[0]=Build(l,mid-1,mid); s[mid].son[1]=Build(mid+1,r,mid); Update(mid); return mid; } char lin[2]; inline void Rotate(int x)//翻上去x { int fx=s[x].fa,ffx=s[fx].fa; bool we=(s[fx].son[1]==x); s[s[x].son[we^1]].fa=fx; s[fx].son[we]=s[x].son[we^1]; s[fx].fa=x; s[x].son[we^1]=fx; s[x].fa=ffx; if(ffx) { s[ffx].son[s[ffx].son[1]==fx]=x; } Update(fx);Update(x); } inline void Splay(int x,int goal) { while(s[x].fa!=goal) { int y=s[x].fa,z=s[y].fa; if(z!=goal) { (s[y].son[1]==x)^(s[z].son[1]==y)?(Rotate(x)):Rotate(y); } Rotate(x); } if(goal==0)rt=x; } inline int Find(int x,int k)//找到在以x为树根的tree第k个次序的节点,不rotate { //chu("find:%d from:%d\n",k,x); if(x==0)return 0; if(s[s[x].son[0]].size==k-1)return x; if(s[s[x].son[0]].size>(k-1))return Find(s[x].son[0],k); return Find(s[x].son[1],k-s[s[x].son[0]].size-1); } inline void Insert(int x,int val)//在第x的位置后面加上一个节点 { int uio=Find(rt,x+1); int iop=Find(rt,x); Splay(iop,0);Splay(uio,iop); s[uio].son[0]=++tot; int bith=s[uio].son[0]; s[bith].size=1; s[bith].w=s[bith].hsh=val; s[bith].fa=uio; Update(uio);Update(iop); } inline void Change(int x,int val) { int xpos=Find(rt,x); Splay(xpos,0); s[xpos].w=val;Update(xpos);//这就完了??? } inline ull Gethsh(int x,int y) { //chu("getfrom:%d to%d\n",x,y); int xpos=Find(rt,x-1),xsub=Find(rt,y+1); // chu("x:上一位:%d 下一位:%d\n",xpos,xsub); Splay(xpos,0); Splay(xsub,xpos); return s[s[xsub].son[0]].hsh; } inline int Make(int x,int y)//找在树里面 { int l=0,r=min(len-x,len-y);//前缀区间长度,不能算我自己加的那个 int mid,ans=l; // chu("x:%d y;%d\n",x,y); while(l<=r) { mid=(l+r)>>1; // chu("mid;%d\n",mid); ull xhsh=Gethsh(x,x+mid-1),yhsh=Gethsh(y,y+mid-1); // chu("hsh:%lld %lld\n",xhsh,yhsh); if(xhsh==yhsh) { l=mid+1;ans=mid; } else r=mid-1; } //chu("x:%d y;%d\n",x,y); // chu("%lld %lld\n",Gethsh(x,x),Gethsh(y,y)); return ans; } int main() { //freopen("exam.txt","r",stdin); p[0]=1; _f(i,1,150004)p[i]=p[i-1]*29;//p[i]:29^i scanf("%s",ss+2); // chu("%s",ss+2); len=strlen(ss+2)+2; int m=re();//区分有没有?不行,算hsh会出错 ss[1]='a'-1,ss[len]='a'-1;//a-1就是0 rt=Build(1,len,0); //_f(i,1,len) // chu("s[%d].w:%d\n",i,s[i].w); tot=len; // chu("root:%d\n",rt); //chu("%d\n",len); _f(i,1,m) { scanf("%s",lin); if(lin[0]=='Q') { int x,y; x=re();y=re(); // chu("xxx:%d yyy:%d\n",x,y); chu("%d\n",Make(x+1,y+1)); } else if(lin[0]=='R') { int x=re();scanf("%s",lin); Change(x+1,lin[0]-'a'+1); } else { int x=re();scanf("%s",lin); Insert(x+1,lin[0]+1-'a');len++; } } return 0; } /* madamimadam madamimadam 7 Q 1 7 Q 4 8 Q 10 11 R 3 a Q 1 7 I 10 a Q 2 11 第一行给出初始的字符串。第二行是一个非负整数M, 表示操作的个数。接下来的M行,每行描述一个操作。 操 作有3种,如下所示 1、询问。语法:Qxy,x,y均为正 整数。功能:计算LCQ(x,y)限制:1<=x,y<=当前字符串长度。 2、修改。语法:Rxd,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。 限制:x不超过当前字 符串长度。 3、插入:语法:Ixd,x是非负整数,d是字符。 功能:在字符串第x个字符之后插入字符d,如果x=0,则在字 符串开头插入。 限制:x不超过当前字符串长度 splay: 中序遍历位置一定 节点维护: h[x]节点子树涵盖区间hash值 size[]节点个数 date[]代表单个hash值 查询: 二分查找 len: l=0,r=nown-(min(x,y))+1 query(x,x+mid-1),query(y,y+mid-1) 比较长度 添加一个点 很特殊,在于我不知道明确的可以比较的val,只能根据size维护相对大小 所以insert需要利用二叉树性质 find(k)//找到第k个 ,k+1个 k是rt,k+1是右son,s[k+1].son[0]一定没有 改变一个点 甩到根节点 直接变data 更新hash */
最后找答案就二分区间
5
DP结合线段树:求最长上升子序列
const int SIZE=100000+100; struct SPLAY { int son[2],f,size; int dp,val; }s[SIZE]; int tot; int n,rt; inline void Update(int x) { s[x].size=1; s[x].dp=s[x].val; if(s[x].son[1]) { s[x].size+=s[s[x].son[1]].size; s[x].dp=max(s[x].dp,s[s[x].son[1]].dp); } if(s[x].son[0]) { s[x].size+=s[s[x].son[0]].size; s[x].dp=max(s[x].dp,s[s[x].son[0]].dp); } } inline void Rotate(int x) { int fx=s[x].f,ffx=s[fx].f; bool w=(s[fx].son[1]==x); s[s[x].son[w^1]].f=fx; s[fx].son[w]=s[x].son[w^1]; s[fx].f=x; s[x].son[w^1]=fx; s[x].f=ffx; if(ffx) { s[ffx].son[s[ffx].son[1]==fx]=x; } Update(fx);Update(x); } inline void Splay(int x,int goal) { while(s[x].f!=goal) { int y=s[x].f,z=s[y].f; if(z!=goal) { (s[y].son[1]==x)^(s[z].son[1]==y)?(Rotate(x)):Rotate(y); } Rotate(x); } if(goal==0)rt=x; } inline int Find(int pos,int x) { // if(pos==0||x==0)return 0; if(x<=s[s[pos].son[0]].size)return Find(s[pos].son[0],x); if(s[s[pos].son[0]].size+1==x)return pos; return Find(s[pos].son[1],x-s[s[pos].son[0]].size-1); } inline void Insert(int x) { int pos=Find(rt,x),sub=Find(rt,x+1); Splay(pos,0);Splay(sub,pos); s[sub].son[0]=++tot; s[tot].f=sub; s[tot].size=s[tot].dp=s[tot].val=1; Update(sub);Update(pos); } int main() { //freopen("exam.txt","r",stdin); //freopen("kap.txt","w",stdout); n=re(); rt=1; tot=0; s[++tot].size=2; tot++; s[tot-1].son[1]=tot; s[tot].size=1;s[tot].f=1; int ans=0; _f(i,1,n) { int kp=re()+1; Insert(kp); Splay(Find(rt,kp+1),0); if(s[rt].val<s[s[rt].son[0]].dp+1)s[rt].val=s[s[rt].son[0]].dp+1; s[rt].dp=max(s[rt].dp,s[rt].val); ans=max(ans,s[rt].val); chu("%d\n",ans); } return 0; }
6给你一棵树,每个点有自己的权值,支持3个操作
(1)Q x:询问从x到根的路径上权值总和
(2)C x y:给x所在子树的每个点权值+y
(3)F x y:让x的父亲变成y
如果没有变换父亲,我们dfs记录每个节点被遍历到的时间戳(两次,一次进去一次出来),进去的权值是点权值,出去的是负点权值,得到一个序列,我们发现一个神奇的性质,从根到x节点的路径之和就是序列date[x.in]的前缀和
因为进去出去的会被抵消,而进去没有出去的说明就在rt--x的路径上,那么就可以用平衡树维护进出序,对于每个节点维护sum,子树的权值和,val,本节点权值,tag懒度标记
对于Q,我们把x的后继旋转到root,那么root的左子树就是答案
对于F,我们把x.in的前驱旋转到root,把x.out的后继旋转到root的右儿子,那么所有会被影响的序列就在后继的左子树里了,直接懒度标记
对于C,我们还是一样的方法把x的子树拎出来,然后直接把x和父亲的关系断掉,再把新的父亲提到root,后继提到rson,后继的lson就直接接上这个子树就行
要注意的就是寻找前驱后继必须先把x提到root
tag标记的更新不仅仅需要把当前节点的下传,还需要把当前节点·到splay的root的全部下传,因为我修改操作由于直接可以通过下标定位,所以相当于有好多在半截修改,这样就需要“一传到底”
const int N=100000+100; int n,w[N],m,tot,etot,head[N],oula,st[N<<1],st_top=1,sk[N<<1],sk_top,id[N<<1],rt; struct PAIR { int in,out; PAIR(){} PAIR(int ini,int outi) { in=ini,out=outi; } }d[N];//记录入出 struct SPLAY { int fa,in_num,out_num,tag; ll val,sum;int son[2]; }t[N<<1]; struct DUG { int to,nxt; }e[N]; inline void Add(int x,int y) { e[++etot].to=y,e[etot].nxt=head[x],head[x]=etot; } inline int Sub(int x) { int now=t[x].son[1]; while(t[now].son[0])now=t[now].son[0]; return now; } inline int Pre(int x) { int now=t[x].son[0]; while(t[now].son[1])now=t[now].son[1]; return now; } inline void Update(int x,ll vl)//把x的值加vl { t[x].val=t[x].val+id[x]*vl; t[x].tag+=vl; t[x].sum=t[x].sum+vl*(t[x].in_num-t[x].out_num); } inline void Pushdown(int x) { if(t[x].tag) { Update(t[x].son[0],t[x].tag); Update(t[x].son[1],t[x].tag); t[x].tag=0; } } inline void Pushup(int x) { t[x].sum=t[t[x].son[0]].sum+t[t[x].son[1]].sum+t[x].val; t[x].in_num=t[t[x].son[0]].in_num+t[t[x].son[1]].in_num+(id[x]==1); t[x].out_num=t[t[x].son[0]].out_num+t[t[x].son[1]].out_num+(id[x]==-1); } inline void Rotate(int x) { int fx=t[x].fa,ffx=t[fx].fa; bool op=(t[fx].son[1]==x); if(ffx) { t[ffx].son[t[ffx].son[1]==fx]=x; } t[x].fa=ffx; t[t[x].son[op^1]].fa=fx; t[fx].son[op]=t[x].son[op^1]; t[x].son[op^1]=fx; t[fx].fa=x; Pushup(fx);Pushup(x); } inline void Splay(int x,int goal) { //chu("splay:%d-->%d\n",x,goal); int u=x; sk[sk_top=1]=u;u=t[u].fa; //chu("u:%d\n",t[u].fa); while(t[u].fa)sk[++sk_top]=u,u=t[u].fa; //chu("sk_tp:%d\n",sk_top); f_(i,sk_top,1)Pushdown(sk[sk_top]),sk_top--;//下放标记 //chu("out\n"); while(t[x].fa!=goal) { int fx=t[x].fa,ffx=t[fx].fa; if(ffx!=goal) { ((t[fx].son[1]==x)^(t[ffx].son[1]==fx))?(Rotate(x)):(Rotate(fx)); } Rotate(x); } if(goal==0)rt=x; } inline void Insert(int x,ll vl) { //chu("tot:%d val:%d\n",tot,vl); while(1) { //chu("dfds\n"); int xfa=t[x].son[1]; if(!xfa) { xfa=++tot; t[xfa].fa=x; t[x].son[1]=xfa; if(id[tot]==-1)t[tot].in_num=1; else if(id[tot]==1)t[tot].out_num=1; t[tot].val=t[tot].sum=vl;x=xfa;//是这样吧? break; } x=xfa; } //chu("splay(%d-->%d)\n",x,rt); Splay(x,0);//把x转成根 } inline void dfs(int x) { //chu("递归到:%d(tot:%d)\n",x,tot); st[++st_top]=x;d[x].in=st_top;id[st_top]=1; Insert(rt,w[x]); for(rint i=head[x];i;i=e[i].nxt) { int to=e[i].to; dfs(to); } st[++st_top]=x;d[x].out=st_top;id[st_top]=-1; Insert(rt,-w[x]); } inline void Change(int x,int fax)//这是.... { Splay(d[x].in,0); int a=Pre(d[x].in); Splay(d[x].out,0); int b=Sub(d[x].out); Splay(a,0); Splay(b,rt); //chu("原来的前驱:%d 后继:%d\n",a,b); int goal=t[b].son[0]; t[b].son[0]=0; //chu("goal:%d son:%d\n",goal,t[goal].son[0]); Pushup(b);Pushup(rt); Splay(d[fax].in,0); int uu=Sub(d[fax].in); Splay(uu,rt); t[uu].son[0]=goal; t[goal].fa=uu; Pushup(uu);Pushup(rt); } inline ll Query(int x) { Splay(d[x].in,0); int a=Sub(d[x].in); Splay(a,0); //chu("root:%d x:%d\n",rt,x); return t[t[a].son[0]].sum; } inline void Add_val(int x,ll vl) { Splay(d[x].in,0); //chu("din%d \n",d[x].in); int a=Pre(d[x].in); Splay(d[x].out,0); int b=Sub(d[x].out); Splay(a,0); Splay(b,a); //chu("%d--%d\n",a,b); int goal=t[b].son[0];//子树 t[goal].val+=vl*id[goal]; //chu("let %d add:%lld\n",goal,vl*id[goal]); t[goal].tag+=vl; } char gh[4]; int main() { //freopen("exam.txt","r",stdin); //freopen("kap.txt","w",stdout); n=re(); tot=1; rt=1; //chu("df\n"); _f(i,2,n) { int x=re();Add(x,i); } //chu("thetn:%d\n",tot); _f(i,1,n)w[i]=re(); dfs(1); // _f(i,1,5) // chu("in;%d(%d) out:%d(%d)\n",d[i].in,t[d[i].in].val,d[i].out,t[d[i].out].val); // chu("out\n"); Insert(rt,0); m=re(); _f(i,1,m) { scanf("%s",gh); if(gh[0]=='Q') { int di=re(); chu("%lld \n",Query(di)); } else if(gh[0]=='C') { int xi=re(),yi=re(); Change(xi,yi); } else { int pi=re(),qi=re(); Add_val(pi,qi); } } return 0; }