【做题记录】2025刷题计划—数据结构1

A. 【模板】线段树分裂

首先想到了 FHQ-Treap,但是合并的时候还需要满足大小顺序,不太好搞。考虑权值线段树。
\(1\)\(4\) 操作都是比较常规的。对于 \(0\) 操作,在 \(p\) 的线段树上将 \([x,y]\) 这个区间拆成 \(O(\log n)\) 个区间,赋给新的线段树。注意当找到区间时需要将 \(p\) 的线段树上的信息清掉。
时间复杂度分析,\(0\)\(2\)\(3\)\(4\) 操作单次的复杂度都是 \(O(\log n)\) 的,而所有的 \(1\) 操作时间复杂度之和不会超过总点数,即 \(O((n+m)\log n)\)。所以时间复杂度就是 \(O((n+m)\log n)\)。空间带一个 \(2\) 的常数。

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
#include<bits/stdc++.h> #define ll long long #define il inline #define ls(id) tr[id].ls #define rs(id) tr[id].rs #define sz(id) tr[id].sz using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=2e5+5; int n,m,rt[maxn],tot,cnt=1; struct{ int ls,rs; ll sz; }tr[maxn*40]; il void pushup(int id){ sz(id)=sz(ls(id))+sz(rs(id)); } il void insert(int &id,int l,int r,int x,int v){ if(!id){ id=++tot; } if(l==r){ sz(id)+=v; return ; } int mid=(l+r)>>1; if(x<=mid){ insert(ls(id),l,mid,x,v); } else{ insert(rs(id),mid+1,r,x,v); } pushup(id); } il int move(int &p,int L,int R,int l,int r){ if(!p){ return 0; } if(L>=l&&R<=r){ int q=p; p=0; return q; } int q=++tot,mid=(L+R)>>1; if(l<=mid){ ls(q)=move(ls(p),L,mid,l,r); } if(r>mid){ rs(q)=move(rs(p),mid+1,R,l,r); } pushup(p),pushup(q); return q; } il int merge(int p,int q,int l,int r){ if(!p||!q){ return p+q; } if(l==r){ sz(++tot)=sz(p)+sz(q); return tot; } int x=++tot,mid=(l+r)>>1; ls(x)=merge(ls(p),ls(q),l,mid); rs(x)=merge(rs(p),rs(q),mid+1,r); pushup(x); return x; } il ll query(int id,int L,int R,int l,int r){ if(!id){ return 0; } if(L>=l&&R<=r){ return sz(id); } int mid=(L+R)>>1; ll res=0; if(l<=mid){ res+=query(ls(id),L,mid,l,r); } if(r>mid){ res+=query(rs(id),mid+1,R,l,r); } return res; } il int kth(int id,int l,int r,ll k){ if(l==r){ return l; } int mid=(l+r)>>1; if(sz(ls(id))>=k){ return kth(ls(id),l,mid,k); } return kth(rs(id),mid+1,r,k-sz(ls(id))); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m; for(int i=1,v;i<=n;i++){ cin>>v; insert(rt[1],1,n,i,v); } while(m--){ int opt; cin>>opt; switch(opt){ case 0:{ int p,l,r; cin>>p>>l>>r; rt[++cnt]=move(rt[p],1,n,l,r); break; } case 1:{ int p,q; cin>>p>>q; rt[p]=merge(rt[p],rt[q],1,n); rt[q]=0; break; } case 2:{ int p,x,v; cin>>p>>v>>x; insert(rt[p],1,n,x,v); break; } case 3:{ int p,l,r; cin>>p>>l>>r; cout<<query(rt[p],1,n,l,r)<<"\n"; break; } default:{ int p; ll k; cin>>p>>k; if(sz(rt[p])<k){ cout<<"-1\n"; } else{ cout<<kth(rt[p],1,n,k)<<"\n"; } break; } } } return 0; } } int main(){return asbt::main();}

B. 三元上升子序列

考虑这个三元上升子序列,可以拆成两个二元来做。
考虑 FHQ-Treap,依次插入 \(a_i\),将当前的平衡树按 \(a_i-1\) 分裂后左子树的大小就是以 \(a_i\) 结尾的二元上升子序列数,记为 \(num\)。而左子树的 \(num\) 之和就是以 \(a_i\) 结尾的三元上升子序列数了。时间复杂度 \(O(n\log n)\)

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
#include<bits/stdc++.h> #define ll long long #define il inline using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=3e4+5; int n,rt,tot,sz[maxn]; int ls[maxn],rs[maxn]; int zhi[maxn],jan[maxn]; ll num[maxn],sum[maxn]; il void pushup(int id){ sum[id]=sum[ls[id]]+sum[rs[id]]+num[id]; sz[id]=sz[ls[id]]+sz[rs[id]]+1; } il int merge(int p,int q){ if(!p||!q){ return p+q; } if(jan[p]<jan[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 &p,int &q,int x){ if(!id){ p=q=0; return ; } if(zhi[id]<=x){ p=id; split(rs[id],rs[id],q,x); } else{ q=id; split(ls[id],p,ls[id],x); } pushup(id); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n; ll ans=0; while(n--){ int v,x,y; cin>>v; split(rt,x,y,v-1); ans+=sum[x]; zhi[++tot]=v; num[tot]=sum[tot]=sz[x]; jan[tot]=rand(); sz[tot]=1; rt=merge(merge(x,tot),y); } cout<<ans; return 0; } } int main(){return asbt::main();}

C. Sanae and Giant Robot

csp2025-提高组并查集专题 E

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
#include<bits/stdc++.h> #define int long long #define il inline #define pb push_back #define lwrb lower_bound using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=2e5+5; int T,n,m; int a[maxn],b[maxn]; vector<int> e[maxn]; set<int> zero; queue<int> q; il void solve(){ cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1;i<=n;i++){ cin>>b[i]; } while(m--){ int l,r; cin>>l>>r; l--; e[l].pb(r),e[r].pb(l); } for(int i=1;i<=n;i++){ a[i]=a[i-1]+a[i]-b[i]; } for(int i=0;i<=n;i++){ if(a[i]){ zero.insert(i); } else{ q.push(i); } } while(q.size()){ int u=q.front(); q.pop(); // puts("666"); for(int v:e[u]){ if(a[v]){ continue; } int l=u,r=v; if(l>r){ swap(l,r); } auto x=zero.lwrb(l); while(x!=zero.end()&&*x<=r){ a[*x]=0; q.push(*x); x=zero.erase(x); } } } puts(zero.size()?"NO":"YES"); zero.clear(); for(int i=0;i<=n;i++){ e[i].clear(); } } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } signed main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>T; while(T--){ solve(); } return 0; } } signed main(){return asbt::main();}

D. [COCI2010-2011#6] STEP

线段树,对于每个区间维护区间长度,最左端颜色,最右端颜色,紧贴左侧的最长 01 串,紧贴右侧的最长 01 串和区间内的最长 01 串。pushup 时考察左区间的右端颜色和右区间的左端颜色是否不同。

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
#include<bits/stdc++.h> #define ll long long #define il inline #define lid id<<1 #define rid id<<1|1 #define len(id) tr[id].len #define lc(id) tr[id].lc #define rc(id) tr[id].rc #define lk(id) tr[id].lk #define rk(id) tr[id].rk #define mk(id) tr[id].mk using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=2e5+5; int n,m; struct node{ int len,lc,rc; int lk,rk,mk; il node operator+(const node &x)const{ node res; res.len=len+x.len; res.lc=lc,res.rc=x.rc; res.lk=lk,res.rk=x.rk; res.mk=max(mk,x.mk); if(rc^x.lc){ res.mk=max(res.mk,rk+x.lk); if(mk==len){ res.lk=max(res.lk,len+x.lk); } if(x.mk==x.len){ res.rk=max(res.rk,rk+x.len); } } return res; } }tr[maxn<<2]; il void pushup(int id){ tr[id]=tr[lid]+tr[rid]; } il void build(int id,int l,int r){ if(l==r){ len(id)=1; lc(id)=rc(id)=0; lk(id)=rk(id)=mk(id)=1; return ; } int mid=(l+r)>>1; build(lid,l,mid); build(rid,mid+1,r); pushup(id); } il void upd(int id,int l,int r,int p){ if(l==r){ lc(id)^=1,rc(id)^=1; return ; } int mid=(l+r)>>1; if(p<=mid){ upd(lid,l,mid,p); } else{ upd(rid,mid+1,r,p); } pushup(id); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m; build(1,1,n); while(m--){ int p; cin>>p; upd(1,1,n,p); cout<<mk(1)<<"\n"; } return 0; } } int main(){return asbt::main();}

E. [USACO11DEC] Grass Planting G

树剖 + 线段树,区间加单点查。可以标记永久化。

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
#include<bits/stdc++.h> #define ll long long #define il inline #define pb push_back #define lid id<<1 #define rid id<<1|1 using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e5+5; int n,m,cnt,tr[maxn<<2]; int fa[maxn],sz[maxn]; int hes[maxn],dep[maxn]; int top[maxn],dfn[maxn]; vector<int> e[maxn]; il void dfs1(int u){ sz[u]=1; int mxs=0; for(int v:e[u]){ if(v==fa[u]){ continue; } fa[v]=u,dep[v]=dep[u]+1; dfs1(v); sz[u]+=sz[v]; if(mxs<sz[v]){ mxs=sz[v],hes[u]=v; } } } il void dfs2(int u){ dfn[u]=++cnt; if(!top[u]){ top[u]=u; } if(hes[u]){ top[hes[u]]=top[u]; dfs2(hes[u]); } for(int v:e[u]){ if(v==fa[u]||v==hes[u]){ continue; } dfs2(v); } } il void upd(int id,int L,int R,int l,int r){ // cout<<l<<" "<<r<<"\n"; if(l>r){ return ; } if(L>=l&&R<=r){ tr[id]++; return ; } int mid=(L+R)>>1; if(l<=mid){ upd(lid,L,mid,l,r); } if(r>mid){ upd(rid,mid+1,R,l,r); } } il int query(int id,int l,int r,int p){ int res=tr[id]; if(l==r){ return res; } int mid=(l+r)>>1; if(p<=mid){ return res+query(lid,l,mid,p); } return res+query(rid,mid+1,r,p); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m; for(int i=1,u,v;i<n;i++){ cin>>u>>v; e[u].pb(v),e[v].pb(u); } dfs1(1),dfs2(1); while(m--){ char opt; int u,v; cin>>opt>>u>>v; if(opt=='P'){ // cout<<u<<" "<<v<<"\n"; // for(int i=1;i<=n;i++){ // cout<<top[i]<<" "; // } // puts(""); // cout<<top[u]<<" "<<top[v]<<"\n"; while(top[u]!=top[v]){ // puts("666"); if(dep[top[u]]<dep[top[v]]){ swap(u,v); } // cout<<u<<"\n"; upd(1,1,n,dfn[top[u]],dfn[u]); u=fa[top[u]]; } // cout<<u<<" "<<v<<"\n"; if(dep[u]>dep[v]){ swap(u,v); } upd(1,1,n,dfn[u]+1,dfn[v]); } else{ if(dep[u]<dep[v]){ swap(u,v); } cout<<query(1,1,n,dfn[u])<<"\n"; } } return 0; } } int main(){return asbt::main();}

F. [NOI2021] 轻重边

对于每次修改,将这个路径上的所有点都赋一个新的权值。于是两端权值相等的就是重边,不等的就是轻边。树剖 + 线段树维护即可。

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
#include<bits/stdc++.h> #define ll long long #define il inline #define pb push_back #define lid id<<1 #define rid id<<1|1 #define lc(id) tr[id].lc #define rc(id) tr[id].rc #define num(id) tr[id].num using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e5+5; int T,n,m,cnt,tag[maxn<<2]; int sz[maxn],hes[maxn]; int dep[maxn],top[maxn]; int dfn[maxn],fa[maxn]; vector<int> e[maxn]; struct node{ int lc,rc,num; il node operator+(const node &x)const{ node res; res.lc=lc,res.rc=x.rc; res.num=num+x.num; if(rc==x.lc){ res.num++; } return res; } }tr[maxn<<2]; il void dfs1(int u){ sz[u]=1; int mxs=0; for(int v:e[u]){ if(v!=fa[u]){ fa[v]=u; dep[v]=dep[u]+1; dfs1(v); sz[u]+=sz[v]; if(mxs<sz[v]){ hes[u]=v; mxs=sz[v]; } } } } il void dfs2(int u){ dfn[u]=++cnt; if(!top[u]){ top[u]=u; } if(hes[u]){ top[hes[u]]=top[u]; dfs2(hes[u]); } for(int v:e[u]){ if(v!=fa[u]&&v!=hes[u]){ dfs2(v); } } } il void pushup(int id){ tr[id]=tr[lid]+tr[rid]; } il void pushtag(int id,int v,int len){ lc(id)=rc(id)=tag[id]=v; num(id)=len-1; } il void pushdown(int id,int len){ if(tag[id]){ pushtag(lid,tag[id],(len+1)>>1); pushtag(rid,tag[id],len>>1); tag[id]=0; } } il void build(int id,int l,int r){ tag[id]=0; if(l==r){ pushtag(id,l,1); return ; } int mid=(l+r)>>1; build(lid,l,mid); build(rid,mid+1,r); pushup(id); } il void upd(int id,int L,int R,int l,int r,int v){ if(L>=l&&R<=r){ pushtag(id,v,R-L+1); return ; } pushdown(id,R-L+1); int mid=(L+R)>>1; if(l<=mid){ upd(lid,L,mid,l,r,v); } if(r>mid){ upd(rid,mid+1,R,l,r,v); } pushup(id); } il node query(int id,int L,int R,int l,int r){ if(L>=l&&R<=r){ return tr[id]; } pushdown(id,R-L+1); int mid=(L+R)>>1; if(r<=mid){ return query(lid,L,mid,l,r); } if(l>mid){ return query(rid,mid+1,R,l,r); } return query(lid,L,mid,l,r)+query(rid,mid+1,R,l,r); } il void solve(){ cin>>n>>m; for(int i=1,u,v;i<n;i++){ cin>>u>>v; e[u].pb(v),e[v].pb(u); } cnt=0; dfs1(1),dfs2(1); // for(int i=1;i<=n;i++){ // cout<<fa[i]<<" "; // } // puts(""); build(1,1,n); int tot=n; while(m--){ int opt,u,v; cin>>opt>>u>>v; if(opt==1){ tot++; while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]){ swap(u,v); } upd(1,1,n,dfn[top[u]],dfn[u],tot); u=fa[top[u]]; } if(dep[u]>dep[v]){ swap(u,v); } upd(1,1,n,dfn[u],dfn[v],tot); } else{ node resu,resv; bool flu=0,flv=0; while(top[u]!=top[v]){ if(dep[top[u]]>dep[top[v]]){ if(!flu){ flu=1; resu=query(1,1,n,dfn[top[u]],dfn[u]); } else{ resu=query(1,1,n,dfn[top[u]],dfn[u])+resu; } u=fa[top[u]]; } else{ if(!flv){ flv=1; resv=query(1,1,n,dfn[top[v]],dfn[v]); } else{ resv=query(1,1,n,dfn[top[v]],dfn[v])+resv; } v=fa[top[v]]; } } if(dep[u]<dep[v]){ if(!flv){ flv=1; resv=query(1,1,n,dfn[u],dfn[v]); } else{ resv=query(1,1,n,dfn[u],dfn[v])+resv; } } else{ if(!flu){ flu=1; resu=query(1,1,n,dfn[v],dfn[u]); } else{ resu=query(1,1,n,dfn[v],dfn[u])+resu; } } if(!flu){ cout<<resv.num<<"\n"; } else if(!flv){ cout<<resu.num<<"\n"; } else{ int res=resu.num+resv.num; if(resu.lc==resv.lc){ res++; } cout<<res<<"\n"; } } } for(int i=1;i<=n;i++){ e[i].clear(); sz[i]=hes[i]=dfn[i]=fa[i]=dep[i]=top[i]=0; } } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>T; while(T--){ solve(); } return 0; } } int main(){return asbt::main();}

G. 「TOCO Round 1」History

看到“回到 \(x\) 后的状态”,显然考虑可持久化线段树。记开灯的为 \(1\),关灯的为 \(0\),对于每个询问只需去查某些点的权值和即可。
考虑怎么将符合条件的点转化为区间。询问的是同一深度的点,可以考虑 bfs 序。考虑如果 \(y\) 是奇数,显然答案为 \(0\);否则询问的点就是 \(x\)\(\frac{y}{2}\) 级祖先在 \(x\) 这一层的后代,再去掉 \(x\)\(\frac{y}{2}-1\) 级祖先在 \(x\) 这一层的后代。考虑怎么去找到询问对应的区间。对于同一棵子树的同一深度,其 bfs 序是连续的。因此可以将询问先存下来,对于每个节点建线段树,存储某一深度的 bfs 序最大值和最小值。线段树合并即可。
时间复杂度分析,处理操作时每次都为 \(O(\log n)\),所有线段树合并的总时间复杂度为 \(O(n\log n)\),于是总的时间复杂度就为 \(O((n+m)\log n)\)

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
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
#include<bits/stdc++.h> #define ll long long #define il inline #define pb push_back #define pii pair<int,int> #define mp make_pair #define fir first #define sec second using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e5+5; const int inf=0x3f3f3f3f; int n,m,dep[maxn]; int anc[maxn][22]; int bfn[maxn]; queue<int> q; vector<int> e[maxn]; vector<pii> quj[maxn]; struct{ int opt,x,y; int l1,r1,l2,r2; }wt[maxn]; il void dfs1(int u,int fa){ dep[u]=dep[fa]+1; anc[u][0]=fa; for(int i=1;i<=20;i++){ anc[u][i]=anc[anc[u][i-1]][i-1]; } for(int v:e[u]){ if(v==fa){ continue; } dfs1(v,u); } } il int ganc(int u,int k){ int tmp=0; while(k){ if(k&1){ u=anc[u][tmp]; } k>>=1,tmp++; } return u; } il void bfs(){ int cnt=0; q.push(1); while(q.size()){ int u=q.front(); q.pop(),bfn[u]=++cnt; for(int v:e[u]){ if(!bfn[v]){ q.push(v); } } } } int rt[maxn],tot,ls[maxn*40],rs[maxn*40],sum[maxn*40],mn[maxn*40],mx[maxn*40]; il int New(){ tot++; ls[tot]=rs[tot]=sum[tot]; mn[tot]=inf,mx[tot]=-inf; return tot; } il void pushup(int id){ sum[id]=sum[ls[id]]+sum[rs[id]]; } il void build(int &id,int l,int r){ id=New(); if(l==r){ return ; } int mid=(l+r)>>1; build(ls[id],l,mid); build(rs[id],mid+1,r); } il void add(int &id,int l,int r,int p,int v){ if(!id){ id=New(); } if(l==r){ mn[id]=min(mn[id],v); mx[id]=max(mx[id],v); return ; } int mid=(l+r)>>1; if(p<=mid){ add(ls[id],l,mid,p,v); } else{ add(rs[id],mid+1,r,p,v); } mn[id]=min(mn[ls[id]],mn[rs[id]]); mx[id]=max(mx[ls[id]],mx[rs[id]]); } il void upd(int &id,int fr,int l,int r,int p){ id=New(); if(l==r){ sum[id]=sum[fr]^1; return ; } int mid=(l+r)>>1; if(p<=mid){ rs[id]=rs[fr]; upd(ls[id],ls[fr],l,mid,p); } else{ ls[id]=ls[fr]; upd(rs[id],rs[fr],mid+1,r,p); } pushup(id); } il int merge(int p,int q,int l,int r){ if(!p||!q){ return p+q; } if(l==r){ int x=New(); mn[x]=min(mn[p],mn[q]); mx[x]=max(mx[p],mx[q]); return x; } int mid=(l+r)>>1,x=New(); ls[x]=merge(ls[p],ls[q],l,mid); rs[x]=merge(rs[p],rs[q],mid+1,r); mn[x]=min(mn[ls[x]],mn[rs[x]]); mx[x]=max(mx[ls[x]],mx[rs[x]]); return x; } il int query(int id,int L,int R,int l,int r){ if(l>r||!id){ return 0; } if(L>=l&&R<=r){ return sum[id]; } int mid=(L+R)>>1,res=0; if(l<=mid){ res+=query(ls[id],L,mid,l,r); } if(r>mid){ res+=query(rs[id],mid+1,R,l,r); } return res; } il int qmn(int id,int l,int r,int p){ if(l==r){ return mn[id]; } int mid=(l+r)>>1; return p<=mid?qmn(ls[id],l,mid,p):qmn(rs[id],mid+1,r,p); } il int qmx(int id,int l,int r,int p){ if(l==r){ return mx[id]; } int mid=(l+r)>>1; return p<=mid?qmx(ls[id],l,mid,p):qmx(rs[id],mid+1,r,p); } il void dfs2(int u,int fa){ add(rt[u],1,n,dep[u],bfn[u]); for(int v:e[u]){ if(v==fa){ continue; } dfs2(v,u); rt[u]=merge(rt[u],rt[v],1,n); } for(pii v:quj[u]){ int k=v.fir,id=v.sec; if(id>0){ wt[id].l1=qmn(rt[u],1,n,k); wt[id].r1=qmx(rt[u],1,n,k); } else{ id=-id; wt[id].l2=qmn(rt[u],1,n,k); wt[id].r2=qmx(rt[u],1,n,k); } } } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n; for(int i=1,u,v;i<n;i++){ cin>>u>>v; e[u].pb(v),e[v].pb(u); } dfs1(1,0),bfs(); cin>>m; for(int i=1,opt,x,y;i<=m;i++){ cin>>opt>>x; wt[i].opt=opt,wt[i].x=x; if(opt==2){ cin>>y; wt[i].y=y; if((y&1)||!y){ continue; } y>>=1; if(y>=dep[x]){ continue; } x=ganc(wt[i].x,y); quj[x].pb(mp(dep[wt[i].x],i)); x=ganc(wt[i].x,y-1); quj[x].pb(mp(dep[wt[i].x],-i)); } } mn[0]=inf,mx[0]=-inf; dfs2(1,0); tot=0; build(rt[0],1,n); for(int i=1,opt,x,y;i<=m;i++){ opt=wt[i].opt,x=wt[i].x,y=wt[i].y; switch(opt){ case 1:{ upd(rt[i],rt[i-1],1,n,bfn[x]); break; } case 2:{ rt[i]=rt[i-1]; if(y&1){ cout<<"0\n"; } else if(!y){ cout<<query(rt[i],1,n,bfn[x],bfn[x])<<"\n"; } else{ y>>=1; if(y>=dep[x]){ cout<<"0\n"; } else{ cout<<query(rt[i],1,n,wt[i].l1,wt[i].l2-1)+query(rt[i],1,n,wt[i].r2+1,wt[i].r1)<<"\n"; } } break; } default:{ rt[i]=rt[x]; break; } } } return 0; } } int main(){return asbt::main();}

H. Physical Education Lessons

颜色段均摊 + 区间求和,使用珂朵莉树,动态维护答案。时间复杂度 \(O(m\log m)\)。因为全是推平操作所以时间复杂度正确。

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
#include<bits/stdc++.h> #define ll long long #define il inline #define lwrb lower_bound using namespace std; namespace asbt{ namespace cplx{bool begin;} int n,m,ans; struct node{ int l,r,v; node(int l=0,int r=0,int v=0):l(l),r(r),v(v){} il bool operator<(const node &x)const{ return l<x.l; } }; set<node> odt; il void build(){ odt.insert(node(1,n+1,1)); ans=n; } il auto split(int x){ auto it=odt.lwrb(x); if(it!=odt.end()&&it->l==x){ return it; } it--; int l=it->l,r=it->r,v=it->v; odt.erase(it); odt.insert(node(l,x-1,v)); return odt.insert(node(x,r,v)).first; } il void assign(int l,int r,int v){ auto itr=split(r+1); auto itl=split(l); for(auto it=itl;it!=itr;it++){ ans-=it->v*(it->r-it->l+1); } odt.erase(itl,itr); odt.insert(node(l,r,v)); ans+=v*(r-l+1); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m; build(); while(m--){ int l,r,k; cin>>l>>r>>k; k--; assign(l,r,k); cout<<ans<<"\n"; } return 0; } } int main(){return asbt::main();}

I. Points

将横坐标离散化,对每个横坐标开一个 set,维护这个横坐标上的所有纵坐标,再开一个维护区间最大值的线段树。修改直接在 set 和线段树上修改即可。对于查询,首先找到最小的满足大于 \(x\) 且有纵坐标大于 \(y\) 的横坐标 \(p\),再在 \(p\)setupper_bound 即可。

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 lid id<<1 #define rid id<<1|1 #define lwrb lower_bound #define uprb upper_bound using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=2e5+5; int n,lsh[maxn],tot,zhi[maxn<<2]; set<int> dv[maxn]; struct{ int opt,x,y; }wt[maxn]; il void pushup(int id){ zhi[id]=max(zhi[lid],zhi[rid]); } il void build(int id,int l,int r){ if(l==r){ dv[l].insert(-1); zhi[id]=-1; return ; } int mid=(l+r)>>1; build(lid,l,mid); build(rid,mid+1,r); pushup(id); } il void insert(int id,int l,int r,int x,int y){ if(l==r){ dv[x].insert(y); zhi[id]=*dv[x].rbegin(); return ; } int mid=(l+r)>>1; if(x<=mid){ insert(lid,l,mid,x,y); } else{ insert(rid,mid+1,r,x,y); } pushup(id); } il void erase(int id,int l,int r,int x,int y){ if(l==r){ dv[x].erase(y); zhi[id]=*dv[x].rbegin(); return ; } int mid=(l+r)>>1; if(x<=mid){ erase(lid,l,mid,x,y); } else{ erase(rid,mid+1,r,x,y); } pushup(id); } il int query(int id,int l,int r,int x,int y){ if(x>=r||zhi[id]<=y){ return -1; } if(l==r){ return l; } int mid=(l+r)>>1; int res=query(lid,l,mid,x,y); return ~res?res:query(rid,mid+1,r,x,y); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n; for(int i=1;i<=n;i++){ string opt; cin>>opt>>wt[i].x>>wt[i].y; // cout<<opt<<"\n"; wt[i].opt=(opt[0]=='a'?1:(opt[0]=='r'?2:3)); // cout<<wt[i].opt<<" "<<wt[i].x<<" "<<wt[i].y<<"\n"; lsh[++tot]=wt[i].x; } sort(lsh+1,lsh+tot+1); tot=unique(lsh+1,lsh+tot+1)-lsh-1; build(1,1,tot); for(int i=1,opt,x,y;i<=n;i++){ // cout<<i<<"\n"; opt=wt[i].opt,y=wt[i].y; x=lwrb(lsh+1,lsh+tot+1,wt[i].x)-lsh; switch(opt){ case 1:{ insert(1,1,tot,x,y); break; } case 2:{ erase(1,1,tot,x,y); break; } default:{ int p=query(1,1,tot,x,y); if(~p){ cout<<lsh[p]<<" "<<*dv[p].uprb(y)<<"\n"; } else{ cout<<"-1\n"; } break; } } } return 0; } } int main(){return asbt::main();}

J. Two Segments

首先将枚举原排列中的区间转化为枚举值域上的区间。
从小往大对 \(r\) 扫描线,对于每个 \(l\in[1,r)\) 维护将 \([l,r]\) 在原排列中最少要分成多少段。显然只有 \(1\)\(2\) 段才会产生贡献。那么我们用线段树维护值域上的每个 \(l\) 的最小段数,并维护值域区间上的最小值与最小值和最小值加一的数量。考虑加入 \(p_i\),如何影响我们维护的值。首先对于 \(l\in[1,p_i]\),要多出一段。如果 \(p_{i-1}<p_i\),那么对于 \(l\in[1,p_{i-1}]\),会减少一段。\(p_{i+1}\) 同理。直接在线段树上区间修改即可。时间复杂度 \(O(n\log n)\)

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
#include<bits/stdc++.h> #define ll long long #define il inline #define lid id<<1 #define rid id<<1|1 using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=3e5+5; int n,a[maxn],b[maxn],tag[maxn<<2]; struct node{ int zhi,nm1,nm2; node(int zhi=0,int nm1=0,int nm2=0):zhi(zhi),nm1(nm1),nm2(nm2){} il node operator+(const node &x)const{ node res(min(zhi,x.zhi)); if(res.zhi==zhi){ res.nm1+=nm1; res.nm2+=nm2; } else if(res.zhi+1==zhi){ res.nm2+=nm1; } if(res.zhi==x.zhi){ res.nm1+=x.nm1; res.nm2+=x.nm2; } else if(res.zhi+1==x.zhi){ res.nm2+=x.nm1; } return res; } }tr[maxn<<2]; #define zhi(id) tr[id].zhi #define nm1(id) tr[id].nm1 #define nm2(id) tr[id].nm2 il void pushup(int id){ tr[id]=tr[lid]+tr[rid]; } il void pushtag(int id,int v){ tag[id]+=v,zhi(id)+=v; } il void pushdown(int id){ if(tag[id]){ pushtag(lid,tag[id]); pushtag(rid,tag[id]); tag[id]=0; } } il void build(int id,int l,int r){ if(l==r){ nm1(id)=1; return ; } int mid=(l+r)>>1; build(lid,l,mid); build(rid,mid+1,r); pushup(id); } il void add(int id,int L,int R,int l,int r,int v){ if(l>r){ return ; } if(L>=l&&R<=r){ pushtag(id,v); return ; } pushdown(id); int mid=(L+R)>>1; if(l<=mid){ add(lid,L,mid,l,r,v); } if(r>mid){ add(rid,mid+1,R,l,r,v); } pushup(id); } il int query(int id,int L,int R,int l,int r){ if(l>r){ return 0; } if(L>=l&&R<=r){ int res=0; if(zhi(id)<=2){ res+=nm1(id); } if(zhi(id)<=1){ res+=nm2(id); } return res; } pushdown(id); int mid=(L+R)>>1,res=0; if(l<=mid){ res+=query(lid,L,mid,l,r); } if(r>mid){ res+=query(rid,mid+1,R,l,r); } return res; } #undef zhi #undef nm1 #undef nm2 namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; b[a[i]]=i; } ll ans=0; build(1,1,n); for(int i=1;i<=n;i++){ add(1,1,n,1,i,1); if(a[b[i]-1]<i){ add(1,1,n,1,a[b[i]-1],-1); } if(a[b[i]+1]<i){ add(1,1,n,1,a[b[i]+1],-1); } ans+=query(1,1,n,1,i-1); } cout<<ans; return 0; } } int main(){return asbt::main();}
posted @   zhangxy__hp  阅读(3)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开