【学习笔记】FHQ-Treap

一、前言

在今年 NOIP 前学习了 FHQ-Treap(一种平衡树),现在来记一记。

二、定义

Treap=tree+heap。具体的说,Treap 所维护的值满足二叉搜索树的性质,另一个变量优先级满足堆的性质。优先级一般使一个随机数,这使得树的高度保持在 \(O(\log n)\) 水平,二叉搜索树的性质使树可以维护有序的集合或序列。因为 \(O(\log n)\) 的树高,Treap 的操作时间复杂度一般都是 \(O(\log n)\) 的。

FHQ-Treap 又名无旋 Treap,它通过旋转和分裂维护堆的性质。

三、基本操作

1.合并

合并函数 merge(int p,int q) 用于合并以 \(p\) 为根的树和以 \(q\) 为根的树。需要注意的是,合并函数传入的两个参数 \(p\)\(q\) 要求满足二叉搜索树的性质,因此在合并过程中我们只需维护堆的性质即可。

以小根堆为例(其实大根小根都无所谓):

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian(p)<jian(q)){ rs(p)=merge(rs(p),q); pushup(p); return p; } else{ ls(q)=merge(p,ls(q)); pushup(q); return q; } }

2.分裂

分为按值分裂和按排名分裂。以按值分裂为例,函数 split(int id,int val,int &p,int &q)\(id\) 为根的树分成 \(p\)\(q\) 两棵树,其中 \(p\) 中的值都小于等于 \(val\)\(q\) 中的值都大于 \(val\)

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
il void split(int id,int val,int &p,int &q){ if(!id){ p=q=0; return ; } if(val(id)<=val){ p=id; split(rs(id),val,rs(id),q); } else{ q=id; split(ls(id),val,p,ls(id)); } pushup(id); }

其他操作就都很简单了。

四、例题

1.普通平衡树

板子题,维护一个有序的集合。具体讲讲每个操作。

插入

将树按插入的值 \(val\) 分裂,再新建一个值为 \(val\) 的点合并进去。

删除

通过两次分裂,将所有值为 \(val\) 的点搞到一棵树中。忽略这棵树的根节点进行合并即可。

查询排名

将树按 \(val-1\) 分裂,左边的树的大小再加一就是答案。

查询第 \(k\)

可以按照二叉搜索树的方式递归查询。

查询前驱

\(val-1\) 分裂,设左边大小为 \(res\),排名为 \(res\) 的就是前驱。

查询后继

\(val\) 分裂,设左边大小为 \(res\),排名为 \(res+1\) 的就是后继。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } using namespace std; namespace cplx{bool begin;} const int maxn=1e5+5; int n; struct node{ int ls,rs,val,jian,sz; node(int ls=0,int rs=0,int val=0,int jian=0,int sz=0) :ls(ls),rs(rs),val(val),jian(jian),sz(sz){} }; struct FHQtreap{ #define ls(id) tr[id].ls #define rs(id) tr[id].rs #define val(id) tr[id].val #define jian(id) tr[id].jian #define sz(id) tr[id].sz int rt,tot; node tr[maxn]; il int New(int val){ tot++; val(tot)=val; jian(tot)=rand(); sz(tot)=1; return tot; } il void pushup(int id){ sz(id)=sz(ls(id))+sz(rs(id))+1; } il void split(int id,int val,int &p,int &q){ if(!id){ p=q=0; return ; } if(val(id)<=val){ p=id; split(rs(id),val,rs(id),q); } else{ q=id; split(ls(id),val,p,ls(id)); } pushup(id); } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian(p)<jian(q)){ rs(p)=merge(rs(p),q); pushup(p); return p; } else{ ls(q)=merge(p,ls(q)); pushup(q); return q; } } il void insert(int val){ int x,y; split(rt,val,x,y); rt=merge(merge(x,New(val)),y); } il void erase(int val){ int x,y,z; split(rt,val,x,y); split(x,val-1,x,z); rt=merge(merge(x,merge(ls(z),rs(z))),y); } il int rnk(int val){ int x,y; split(rt,val-1,x,y); int res=sz(x)+1; rt=merge(x,y); return res; } il int kth(int id,int k){ if(sz(ls(id))==k-1){ return val(id); } if(sz(ls(id))>=k){ return kth(ls(id),k); } return kth(rs(id),k-sz(ls(id))-1); } il int kth(int val){ return kth(rt,val); } il int pre(int val){ int x,y; split(rt,val-1,x,y); int res=sz(x); rt=merge(x,y); return kth(res); } il int nxt(int val){ int x,y; split(rt,val,x,y); int res=sz(x); rt=merge(x,y); return kth(res+1); } #undef ls #undef rs #undef val #undef jian #undef sz }fhq; namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ srand(time(0)); read(n); while(n--){ int opt,x; read(opt)read(x) switch(opt){ case 1:{ fhq.insert(x); break; } case 2:{ fhq.erase(x); break; } case 3:{ printf("%d\n",fhq.rnk(x)); break; } case 4:{ printf("%d\n",fhq.kth(x)); break; } case 5:{ printf("%d\n",fhq.pre(x)); break; } default:{ printf("%d\n",fhq.nxt(x)); break; } } } return 0; }

2.文艺平衡树

维护序列。考虑翻转区间 \([l,r]\),就是将 \([l,r]\) 中的每个节点的左右儿子都交换。打上懒标记即可。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } #define ls(id) tr[id].ls #define rs(id) tr[id].rs #define val(id) tr[id].val #define jian(id) tr[id].jian #define tag(id) tr[id].tag #define sz(id) tr[id].sz using namespace std; namespace cplx{bool begin;} const int maxn=1e5+5; int n,m,rt,tot; struct node{ int ls,rs,val,sz,jian; bool tag; }tr[maxn]; il int New(int val){ tot++; val(tot)=val; sz(tot)=1; jian(tot)=rand(); return tot; } il void pushup(int id){ sz(id)=sz(ls(id))+sz(rs(id))+1; } il void pushdown(int id){ if(tag(id)){ swap(ls(id),rs(id)); tag(ls(id))^=1,tag(rs(id))^=1; tag(id)=0; } } il void split(int id,int k,int &p,int &q){ if(!id){ p=q=0; return ; } pushdown(id); if(sz(ls(id))>=k){ q=id; split(ls(id),k,p,ls(id)); } else{ p=id; split(rs(id),k-sz(ls(id))-1,rs(id),q); } pushup(id); } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian(p)<jian(q)){ pushdown(p); rs(p)=merge(rs(p),q); pushup(p); return p; } pushdown(q); ls(q)=merge(p,ls(q)); pushup(q); return q; } il void dfs(int id){ if(!id){ return ; } pushdown(id); dfs(ls(id)); printf("%d ",val(id)); dfs(rs(id)); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ srand(time(0)); read(n)read(m); for(int i=1;i<=n;i++){ rt=merge(rt,New(i)); } while(m--){ int l,r; read(l)read(r); int x,y,z; split(rt,r,x,y); split(x,l-1,x,z); tag(z)^=1; rt=merge(merge(x,z),y); } dfs(rt); return 0; }

3.[HNOI2002] 营业额统计

显然对于每一天,答案就是这一天的值和前驱后继的差的最小值。直接维护即可。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } using namespace std; namespace cplx{bool begin;} const int maxn=(1<<15)+5; const int inf=0x3f3f3f3f; int n,rt,tot,ls[maxn],rs[maxn]; int val[maxn],sz[maxn],jian[maxn]; il int New(int x){ tot++; val[tot]=x; sz[tot]=1; jian[tot]=rand(); return tot; } il void pushup(int id){ sz[id]=sz[ls[id]]+sz[rs[id]]+1; } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian[p]<jian[q]){ rs[p]=merge(rs[p],q); pushup(p); return p; } ls[q]=merge(p,ls[q]); pushup(q); return q; } il void split(int id,int x,int &p,int &q){ if(!id){ p=q=0; return ; } if(val[id]<=x){ p=id; split(rs[id],x,rs[id],q); } else{ q=id; split(ls[id],x,p,ls[id]); } pushup(id); } il void insert(int x){ int p,q; split(rt,x,p,q); rt=merge(merge(p,New(x)),q); } il int rnk(int x){ int p,q; split(rt,x-1,p,q); int res=sz[p]+1; rt=merge(p,q); return res; } il int kth(int id,int k){ if(sz[ls[id]]==k-1){ return val[id]; } if(sz[ls[id]]>=k){ return kth(ls[id],k); } return kth(rs[id],k-sz[ls[id]]-1); } il int pre(int x){ int p,q; split(rt,x-1,p,q); int res=sz[p]; rt=merge(p,q); return kth(rt,res); } il int nxt(int x){ int p,q; split(rt,x-1,p,q); int res=kth(q,1); rt=merge(p,q); return res; } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ read(n); insert(-inf),insert(inf); ll ans=0; for(int i=1,x;i<=n;i++){ read(x); if(i==1){ ans+=x; } else{ ans+=min(x-pre(x),nxt(x)-x); } insert(x); } printf("%lld",ans); return 0; }

4.[HNOI2004] 宠物收养场

依然是求前驱后继。要注意的是,需要记录当前平衡树内维护的是宠物还是领养者。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } using namespace std; namespace cplx{bool begin;} const int maxn=8e4+5,mod=1e6; const ll inf=0x3f3f3f3f3f3f3f3f; int n,rt,tot,ls[maxn],rs[maxn]; int sz[maxn],jian[maxn]; ll zhi[maxn]; il int New(ll val){ zhi[++tot]=val; jian[tot]=rand(); sz[tot]=1; return tot; } il void pushup(int id){ sz[id]=sz[ls[id]]+sz[rs[id]]+1; } il void split(int id,ll val,int &p,int &q){ if(!id){ p=q=0; return ; } if(zhi[id]<=val){ p=id; split(rs[id],val,rs[id],q); } else{ q=id; split(ls[id],val,p,ls[id]); } pushup(id); } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian[p]<jian[q]){ rs[p]=merge(rs[p],q); pushup(p); return p; } ls[q]=merge(p,ls[q]); pushup(q); return q; } il void insert(ll val){ int x,y; split(rt,val,x,y); rt=merge(merge(x,New(val)),y); } il void erase(ll val){ int x,y,z; split(rt,val,x,y); split(x,val-1,x,z); rt=merge(merge(x,merge(ls[z],rs[z])),y); } il ll kth(int id,int k){ if(sz[ls[id]]==k-1){ return zhi[id]; } if(sz[ls[id]]>=k){ return kth(ls[id],k); } return kth(rs[id],k-sz[ls[id]]-1); } il ll pre(ll val){ int x,y; split(rt,val,x,y); ll res=kth(x,sz[x]); rt=merge(x,y); return res; } il ll nxt(ll val){ int x,y; split(rt,val-1,x,y); ll res=kth(y,1); rt=merge(x,y); return res; } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ srand(time(0)); insert(-inf),insert(inf); ll ans=0; read(n); bool flag=0; while(n--){ int opt; ll a; read(opt)read(a); if(sz[rt]==2||flag==opt){ insert(a); flag=opt; } else{ ll x=pre(a),y=nxt(a); (ans+=min(a-x,y-a))%=mod; erase(a-x<=y-a?x:y); } } printf("%lld",ans); return 0; }

5.「NOI 2004」郁闷的出纳员

整体修改 + 查询第 \(k\) 小,打懒标记即可。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } using namespace std; namespace cplx{bool begin;} const int maxn=1e5+5; int n,m,rt,tot,ls[maxn],rs[maxn]; int zhi[maxn],jian[maxn]; int tag[maxn],sz[maxn]; il int New(int val){ zhi[++tot]=val; jian[tot]=rand(); sz[tot]=1; return tot; } il void pushup(int id){ sz[id]=sz[ls[id]]+sz[rs[id]]+1; } il void pushdown(int id){ if(tag[id]){ tag[ls[id]]+=tag[id]; tag[rs[id]]+=tag[id]; zhi[ls[id]]+=tag[id]; zhi[rs[id]]+=tag[id]; tag[id]=0; } } il void split(int id,int val,int &p,int &q){ if(!id){ p=q=0; return ; } pushdown(id); if(zhi[id]<=val){ p=id; split(rs[id],val,rs[id],q); } else{ q=id; split(ls[id],val,p,ls[id]); } pushup(id); } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian[p]<jian[q]){ pushdown(p); rs[p]=merge(rs[p],q); pushup(p); return p; } pushdown(q); ls[q]=merge(p,ls[q]); pushup(q); return q; } il void insert(int val){ int x,y; split(rt,val,x,y); rt=merge(merge(x,New(val)),y); } il void erase(int val){ int x,y,z; split(rt,val,x,y); split(x,val-1,x,z); rt=merge(merge(x,merge(ls[z],rs[z])),y); } il int kth(int id,int k){ if(sz[ls[id]]==k-1){ return zhi[id]; } pushdown(id); if(sz[ls[id]]>=k){ return kth(ls[id],k); } return kth(rs[id],k-sz[ls[id]]-1); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ srand(time(0)); read(n)read(m); int ans=0; while(n--){ char opt; int x; scanf(" %c",&opt); read(x); switch(opt){ case 'I':{ if(x>=m){ insert(x); } break; } case 'A':{ if(sz[rt]){ tag[rt]+=x,zhi[rt]+=x; } break; } case 'S':{ if(sz[rt]){ tag[rt]-=x,zhi[rt]-=x; int val=kth(rt,1); while(val<m){ erase(val),ans++; if(sz[rt]){ val=kth(rt,1); } else{ break; } } } break; } default:{ printf("%d\n",x>sz[rt]?-1:kth(rt,sz[rt]-x+1)); break; } } } printf("%d",ans); return 0; }

6.[JSOI2008]火星人prefix

考虑用平衡树维护哈希。那么对于修改操作,就是一个区间加和区间乘的操作了。询问二分即可。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } #define ull unsigned ll using namespace std; namespace cplx{bool begin;} const int maxn=1e5+5; const ull bas1=13331; const ll bas2=13327,mod2=2.5e9+1; int n,m,rt,tot,sz[maxn],ls[maxn],rs[maxn],jian[maxn]; ull pw1[maxn],ha1[maxn],tgj1[maxn],tgc1[maxn]; ll pw2[maxn],ha2[maxn],tgj2[maxn],tgc2[maxn]; char s[maxn],zhi[maxn]; il void pushup(int id){ sz[id]=sz[ls[id]]+sz[rs[id]]+1; } il void pushdown(int id){ if(tgc1[id]!=1){ if(ls[id]){ tgc1[ls[id]]*=tgc1[id]; tgj1[ls[id]]*=tgc1[id]; ha1[ls[id]]*=tgc1[id]; } if(rs[id]){ tgc1[rs[id]]*=tgc1[id]; tgj1[rs[id]]*=tgc1[id]; ha1[rs[id]]*=tgc1[id]; } tgc1[id]=1; } if(tgc2[id]!=1){ if(ls[id]){ (tgc2[ls[id]]*=tgc2[id])%=mod2; (tgj2[ls[id]]*=tgc2[id])%=mod2; (ha2[ls[id]]*=tgc2[id])%=mod2; } if(rs[id]){ (tgc2[rs[id]]*=tgc2[id])%=mod2; (tgj2[rs[id]]*=tgc2[id])%=mod2; (ha2[rs[id]]*=tgc2[id])%=mod2; } tgc2[id]=1; } if(tgj1[id]){ if(ls[id]){ tgj1[ls[id]]+=tgj1[id]; ha1[ls[id]]+=tgj1[id]; } if(rs[id]){ tgj1[rs[id]]+=tgj1[id]; ha1[rs[id]]+=tgj1[id]; } tgj1[id]=0; } if(tgj2[id]){ if(ls[id]){ (tgj2[ls[id]]+=tgj2[id])%=mod2; (ha2[ls[id]]+=tgj2[id])%=mod2; } if(rs[id]){ (tgj2[rs[id]]+=tgj2[id])%=mod2; (ha2[rs[id]]+=tgj2[id])%=mod2; } tgj2[id]=0; } } il void split(int id,int k,int &p,int &q){ if(!id){ p=q=0; return ; } pushdown(id); if(sz[ls[id]]<k){ p=id; split(rs[id],k-sz[ls[id]]-1,rs[id],q); } else{ q=id; split(ls[id],k,p,ls[id]); } pushup(id); } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian[p]<jian[q]){ pushdown(p); rs[p]=merge(rs[p],q); pushup(p); return p; } pushdown(q); ls[q]=merge(p,ls[q]); pushup(q); return q; } il int kth(int id,int k){ if(sz[ls[id]]==k-1){ return id; } pushdown(id); if(sz[ls[id]]<k){ return kth(rs[id],k-sz[ls[id]]-1); } return kth(ls[id],k); } il void upd(int pos,char val){ int x,y,z; split(rt,pos-1,x,y); z=kth(y,1); tgj1[y]+=pw1[pos-1]*(val-zhi[z]); ha1[y]+=pw1[pos-1]*(val-zhi[z]); (tgj2[y]+=(val-zhi[z]+mod2)%mod2*pw2[pos-1])%=mod2; (ha2[y]+=(val-zhi[z]+mod2)%mod2*pw2[pos-1])%=mod2; zhi[z]=val; rt=merge(x,y); } il void insert(int pos,char val){ zhi[++tot]=val; sz[tot]=1; jian[tot]=rand(); tgc1[tot]=tgc2[tot]=1; int x,y,z; split(rt,pos,x,y); z=kth(x,pos); ha1[tot]=ha1[z]+pw1[pos]*val; ha2[tot]=(ha2[z]+pw2[pos]*val)%mod2; if(y){ tgc1[y]*=bas1; (tgc2[y]*=bas2)%=mod2; tgj1[y]=(tgj1[y]-ha1[z])*bas1+ha1[tot]; tgj2[y]=((tgj2[y]-ha2[z]+mod2)%mod2*bas2+ha2[tot])%mod2; ha1[y]=(ha1[y]-ha1[z])*bas1+ha1[tot]; ha2[y]=((ha2[y]-ha2[z]+mod2)%mod2*bas2+ha2[tot])%mod2; } rt=merge(merge(x,tot),y); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ srand(time(0)); pw1[0]=pw2[0]=1; for(int i=1;i<=1e5;i++){ pw1[i]=pw1[i-1]*bas1; pw2[i]=pw2[i-1]*bas2%mod2; } scanf(" %s",s+1); n=strlen(s+1); rt=tot=1; sz[rt]=1,jian[rt]=rand(); tgc1[rt]=tgc2[rt]=1; for(int i=1;i<=n;i++){ insert(i,s[i]); } read(m); while(m--){ char opt; scanf(" %c",&opt); switch(opt){ case 'Q':{ int x,y,p,q; read(x)read(y); if(x>y){ swap(x,y); } p=kth(rt,x); q=kth(rt,y); ull hx1=ha1[p],hy1=ha1[q]; ll hx2=ha2[p],hy2=ha2[q]; int l=0,r=sz[rt]-y; while(l<r){ int mid=(l+r+1)>>1; p=kth(rt,x+mid); q=kth(rt,y+mid); if((ha1[p]-hx1)*pw1[y-x]==ha1[q]-hy1&&(ha2[p]-hx2+mod2)%mod2*pw2[y-x]%mod2==(ha2[q]-hy2+mod2)%mod2){ l=mid; } else{ r=mid-1; } } printf("%d\n",l); break; } case 'R':{ int x; char val; read(x); scanf(" %c",&val); upd(x+1,val); break; } default:{ int x; char val; read(x); scanf(" %c",&val); insert(x+1,val); break; } } } return 0; }

7.[Tjoi2013]最长上升子序列

考虑 DP,设 \(dp_i\) 表示以 \(i\) 结尾的最长上升子序列长度。有方程:

\[dp_i=\max_{j<i\land a_j<a_i} dp_j \]

因为是从小到大插入,我们就只用维护 \(j<i\) 的限制就行了。用平衡树维护序列即可。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } using namespace std; namespace cplx{bool begin;} const int maxn=1e5+5; int n,rt,tot,sz[maxn],zhi[maxn],mx[maxn],jian[maxn],ls[maxn],rs[maxn]; il void pushup(int id){ mx[id]=max({zhi[id],mx[ls[id]],mx[rs[id]]}); sz[id]=sz[ls[id]]+sz[rs[id]]+1; } il void split(int id,int k,int &p,int &q){ if(!id){ p=q=0; return ; } if(sz[ls[id]]<k){ p=id; split(rs[id],k-sz[ls[id]]-1,rs[id],q); } else{ q=id; split(ls[id],k,p,ls[id]); } pushup(id); } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian[p]<jian[q]){ rs[p]=merge(rs[p],q); pushup(p); return p; } ls[q]=merge(p,ls[q]); pushup(q); return q; } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ read(n); srand(time(0)); for(int i=1,p,x,y;i<=n;i++){ read(p); split(rt,p,x,y); sz[++tot]=1,jian[tot]=rand(); mx[tot]=zhi[tot]=mx[x]+1; rt=merge(merge(x,tot),y); printf("%d\n",mx[rt]); } return 0; }

8.二逼平衡树

线段树套平衡树。

在每个线段树的节点上维护一个平衡树,维护这个区间中的值。逐一分析每个操作。

查询排名

考虑排名的本质,就是比 \(val\) 小的值的数量加一。在线段树上将区间拆分成 \(O(\log n)\) 个,再在每个区间中查询累加答案即可。

查询第 \(k\)

这不好直接查,二分即可。

单点修改

在线段树上每个相关的节点修改即可。

查询前驱

前驱就是比 \(val\) 小的值中最大的值。在每个区间内查询并取 \(\max\) 即可。

查询后继

类似前驱。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } #define lid id<<1 #define rid id<<1|1 using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=5e4+5,inf=0x3f3f3f3f; int n,m,a[maxn],rt[maxn<<2]; namespace fhq{ int tot,zhi[maxn*20],sz[maxn*20]; int ls[maxn*20],rs[maxn*20],jian[maxn*20]; int ljt[maxn*20],top; il int New(int val){ int p=top?ljt[top--]:++tot; zhi[p]=val; jian[p]=rand(); sz[p]=1; ls[p]=rs[p]=0; return p; } il void pushup(int id){ sz[id]=sz[ls[id]]+sz[rs[id]]+1; } il void split(int id,int val,int &p,int &q){ if(!id){ p=q=0; return ; } if(zhi[id]<=val){ p=id; split(rs[id],val,rs[id],q); } else{ q=id; split(ls[id],val,p,ls[id]); } pushup(id); } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian[p]<jian[q]){ rs[p]=merge(rs[p],q); pushup(p); return p; } ls[q]=merge(p,ls[q]); pushup(q); return q; } il int rnk(int &id,int val){ int x,y; split(id,val-1,x,y); int res=sz[x]+1; id=merge(x,y); return res; } il void insert(int &id,int val){ int x,y; split(id,val,x,y); id=merge(merge(x,New(val)),y); } il void del(int &id,int val){ int x,y,z; split(id,val,x,y); split(x,val-1,x,z); ljt[++top]=z; id=merge(merge(x,merge(ls[z],rs[z])),y); } il int kth(int id,int k){ if(sz[ls[id]]==k-1){ return zhi[id]; } if(sz[ls[id]]>=k){ return kth(ls[id],k); } return kth(rs[id],k-sz[ls[id]]-1); } il int qanq(int &id,int val){ int x,y; split(id,val-1,x,y); int res=sz[x]?kth(x,sz[x]):-inf; id=merge(x,y); return res; } il int houj(int &id,int val){ int x,y; split(id,val,x,y); int res=sz[y]?kth(y,1):inf; id=merge(x,y); return res; } } il void build(int id,int l,int r){ for(int i=l;i<=r;i++){ fhq::insert(rt[id],a[i]); } if(l==r){ return ; } int mid=(l+r)>>1; build(lid,l,mid); build(rid,mid+1,r); } il int qrnk(int id,int L,int R,int l,int r,int val){ if(L>=l&&R<=r){ return fhq::rnk(rt[id],val); } int mid=(L+R)>>1,res=0; if(l<=mid){ res+=qrnk(lid,L,mid,l,r,val)-1; } if(r>mid){ res+=qrnk(rid,mid+1,R,l,r,val)-1; } return res+1; } il void upd(int id,int l,int r,int pos,int val){ fhq::del(rt[id],a[pos]); fhq::insert(rt[id],val); if(l==r){ return ; } int mid=(l+r)>>1; if(pos<=mid){ upd(lid,l,mid,pos,val); } else{ upd(rid,mid+1,r,pos,val); } } il int qqanq(int id,int L,int R,int l,int r,int val){ if(L>=l&&R<=r){ return fhq::qanq(rt[id],val); } int mid=(L+R)>>1,res=-inf; if(l<=mid){ res=max(res,qqanq(lid,L,mid,l,r,val)); } if(r>mid){ res=max(res,qqanq(rid,mid+1,R,l,r,val)); } return res; } il int qhouj(int id,int L,int R,int l,int r,int val){ if(L>=l&&R<=r){ return fhq::houj(rt[id],val); } int mid=(L+R)>>1,res=inf; if(l<=mid){ res=min(res,qhouj(lid,L,mid,l,r,val)); } if(r>mid){ res=min(res,qhouj(rid,mid+1,R,l,r,val)); } return res; } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ srand(time(0)); read(n)read(m); for(int i=1;i<=n;i++){ read(a[i]); } build(1,1,n); while(m--){ int opt; read(opt); switch(opt){ case 1:{ int l,r,val; read(l)read(r)read(val); printf("%d\n",qrnk(1,1,n,l,r,val)); break; } case 2:{ int l,r,k; read(l)read(r)read(k); int L=-inf,R=inf; while(L<R){ int mid=(L+R+1)>>1; if(qrnk(1,1,n,l,r,mid)<=k){ L=mid; } else{ R=mid-1; } } printf("%d\n",L); break; } case 3:{ int pos,val; read(pos)read(val); upd(1,1,n,pos,val); a[pos]=val; break; } case 4:{ int l,r,val; read(l)read(r)read(val); printf("%d\n",qqanq(1,1,n,l,r,val)); break; } default:{ int l,r,val; read(l)read(r)read(val); printf("%d\n",qhouj(1,1,n,l,r,val)); break; } } } return 0; } } int main(){return asbt::main();}

9.星系探索

考虑给树建一个欧拉序,对于点 \(u\),在进入 \(u\) 的子树时将 \(u\) 加入序列,记位置为 \(dfn_u\);出 \(u\) 的子树是再将 \(u\) 加入序列,记位置为 \(low_u\)

如果在 \(dfn_u\) 记录 \(u\) 的权值,在 \(low_u\) 记录 \(u\) 的权值的负值,那么对于节点 \(u\),欧拉序上 \([1,dfn_u]\) 的和就是根到 \(u\) 的权值之和。

子树加是简单的,考虑子树移动。对应到区间上,其实就是区间平移。那么看起来似乎就很简单了。问题在于,平移后每个节点的排名可能会改变,要再按排名分裂会出问题。考虑对每个节点,再记录它的父亲节点 \(fa\)。那么一个节点当前的排名就可以跳 \(fa\) 来求了。

Code
复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
#include<bits/stdc++.h> #define ll long long #define il inline #define read(x){\ char ch;\ int fu=1;\ while(!isdigit(ch=getchar()))\ fu-=(ch=='-')<<1;\ x=ch&15;\ while(isdigit(ch=getchar()))\ x=(x<<1)+(x<<3)+(ch&15);\ x*=fu;\ } #define int ll #define pb push_back using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=2e5+5; int n,m,dfn[maxn],low[maxn]; int rt,tot,ls[maxn],rs[maxn]; int fa[maxn],zhi[maxn],sum[maxn]; int sz[maxn],jian[maxn],a[maxn]; int col[maxn],tcl[maxn],tag[maxn]; vector<int> e[maxn]; il int New(int x,int y){ jian[++tot]=rand(); sum[tot]=zhi[tot]=x; tcl[tot]=col[tot]=y; sz[tot]=1,fa[tot]=0; return tot; } il void pushup(int id){ sum[id]=sum[ls[id]]+sum[rs[id]]+zhi[id]; tcl[id]=tcl[ls[id]]+tcl[rs[id]]+col[id]; sz[id]=sz[ls[id]]+sz[rs[id]]+1; } il void pushdown(int id){ if(tag[id]){ tag[ls[id]]+=tag[id]; tag[rs[id]]+=tag[id]; zhi[ls[id]]+=col[ls[id]]*tag[id]; zhi[rs[id]]+=col[rs[id]]*tag[id]; sum[ls[id]]+=tag[id]*tcl[ls[id]]; sum[rs[id]]+=tag[id]*tcl[rs[id]]; tag[id]=0; } } il void split(int id,int k,int &p,int &q){ if(!id){ p=q=0; return ; } pushdown(id); if(sz[ls[id]]<k){ p=id; split(rs[id],k-sz[ls[id]]-1,rs[id],q); fa[rs[id]]=id; } else{ q=id; split(ls[id],k,p,ls[id]); fa[ls[id]]=id; } pushup(id); } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jian[p]<jian[q]){ pushdown(p); rs[p]=merge(rs[p],q); fa[rs[p]]=p; pushup(p); return p; } pushdown(q); ls[q]=merge(p,ls[q]); fa[ls[q]]=q; pushup(q); return q; } il void dfs(int u,int fth){ rt=merge(rt,dfn[u]=New(a[u],1)); fa[rt]=0; for(int v:e[u]){ dfs(v,u); } rt=merge(rt,low[u]=New(-a[u],-1)); fa[rt]=0; } il int rnk(int id){ int res=sz[ls[id]]+1; while(fa[id]){ if(id==rs[fa[id]]){ res+=sz[ls[fa[id]]]+1; } id=fa[id]; } return res; } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } signed main(){ read(n); for(int i=2,x;i<=n;i++){ read(x); e[x].pb(i); } for(int i=1;i<=n;i++){ read(a[i]); } dfs(1,0); read(m); while(m--){ char opt; scanf(" %c",&opt); switch(opt){ case 'Q':{ int u,x,y; read(u); split(rt,rnk(dfn[u]),x,y); fa[x]=fa[y]=0; printf("%lld\n",sum[x]); rt=merge(x,y); fa[rt]=0; break; } case 'C':{ int u,v; read(u)read(v); int l=rnk(dfn[u]),r=rnk(low[u]); int tv=rnk(dfn[v]); int x,y,z; split(rt,r,x,y); fa[x]=fa[y]=0; split(x,l-1,x,z); fa[x]=fa[z]=0; if(tv<l){ int w; split(x,tv,x,w); fa[x]=fa[w]=0; rt=merge(merge(x,z),merge(w,y)); fa[rt]=0; } else{ int w; split(y,tv-r,w,y); rt=merge(merge(x,w),merge(z,y)); fa[rt]=0; } break; } default:{ int u,val; read(u)read(val); int l=rnk(dfn[u]),r=rnk(low[u]); int x,y,z; split(rt,r,x,y); split(x,l-1,x,z); tag[z]+=val,zhi[z]+=col[z]*val; sum[z]+=val*tcl[z]; rt=merge(merge(x,z),y); break; } } } return 0; } } signed main(){return asbt::main();}
posted @   zhangxy__hp  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开