【做题记录】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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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 @ 2025-02-15 17:33  zhangxy__hp  阅读(16)  评论(0)    收藏  举报