【做题记录】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
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
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
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
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
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
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
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
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
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\) 的 set
上 upper_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
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
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];
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;
}
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();}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步