pyyz Day7

可持久化数据结构

前置知识:

值域线段树

对每个数开桶

对桶开线段树

一般离散化

字典树

可持久化线段树

全局第k小:值域线段树二分

区间第k小

利用类似前缀和思想

开n个线段树

Tr-Tl-1进行操作

可持久化权值动态开点线段树二分(好抽象的板子)

板子

主席树:可持久化权值线段树

不要开Long Long!!!
不要开Long Long!!!
不要开Long Long!!!

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define lson x<<1
#define rson x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct node{
	int sum;
	int l,r;
	node(){
		sum=l=r=0;
	}
}tr[50000005];
int cnt;
void update(int x){
	tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
}
int a[1000005],n,q,rt[1000005],lsh[1000005];
int build(int l,int r){
	cnt++;
	int x=cnt;
	if(l==r){
		tr[x].sum=a[l];
		return x;
	}
	int mid=(l+r)>>1;
	tr[x].l=build(l,mid);
	tr[x].r=build(mid+1,r);
	update(x);
	return x;
}
int query(int nl,int nr,int r,int l,int k){
	if(nl==nr) return nl;
	int mid=(nl+nr)>>1;
	int cn=tr[tr[r].l].sum-tr[tr[l].l].sum;
	if(cn>=k) return query(nl,mid,tr[r].l,tr[l].l,k);
	else return query(mid+1,nr,tr[r].r,tr[l].r,k-cn);
}
int modd(int l,int r,int x,int p,int v){
	cnt++;
	int pq=cnt;
	tr[pq]=tr[x];
	if(l==r){
		tr[pq].sum+=v;
		return pq;
	}
	int mid=(l+r)>>1;
	if(p<=mid) tr[pq].l=modd(l,mid,tr[pq].l,p,v);
	else tr[pq].r=modd(mid+1,r,tr[pq].r,p,v);
	update(pq);
	return pq;
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	n=read(),q=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		lsh[i]=a[i];
	}
	sort(lsh+1,lsh+n+1);
	int le=unique(lsh+1,lsh+n+1)-lsh-1;
	rt[0]=build(1,le);
	for(int i=1;i<=n;i++){
		int p=lower_bound(lsh+1,lsh+le+1,a[i])-lsh;
		rt[i]=modd(1,le,rt[i-1],p,1);
	}
	for(int i=1;i<=q;i++){
		int l=read(),r=read(),k=read();
		int p=query(1,le,rt[r],rt[l-1],k);
		cout<<lsh[p]<<'\n';
	}
	return 0;
}

T1 Count on a tree

考虑主席树+LCA

主席树维护每个点到根的链的信息

从fa[u]开新的线段树

答案是Tu+Tv-2*Tlca+lca

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define lson x<<1
#define rson x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct node{
	int sum;
	int l,r;
	node(){
		sum=l=r=0;
	}
}tr[5000005];
int cnt;
void update(int x){
	tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
}
int a[100005],n,q,rt[100005],lsh[100005];
vector<int> tu[100005];
int dep[500006],f[500005][25],le;
int build(int l,int r){
	cnt++;
	int x=cnt;
	if(l==r){
		tr[x].sum=a[l];
		return x;
	}
	int mid=(l+r)>>1;
	tr[x].l=build(l,mid);
	tr[x].r=build(mid+1,r);
	update(x);
	return x;
}

int modd(int l,int r,int x,int p,int v){
	cnt++;
	int pq=cnt;
	tr[pq]=tr[x];
	if(l==r){
		tr[pq].sum+=v;
		return pq;
	}
	int mid=(l+r)>>1;
	if(p<=mid) tr[pq].l=modd(l,mid,tr[pq].l,p,v);
	else tr[pq].r=modd(mid+1,r,tr[pq].r,p,v);
	update(pq);
	return pq;
}
void dfs(int x,int fa){
	int qq=lower_bound(lsh+1,lsh+le+1,a[x])-lsh;
	rt[x]=modd(1,le,rt[fa],qq,1);
	dep[x]=dep[fa]+1;
	for(int i=0;i<=20;i++){
		f[x][i+1]=f[f[x][i]][i];
	}
	for(auto ed:tu[x]){
		if(ed==fa) continue;
		f[ed][0]=x;
		dfs(ed,x);
	}
}
int lca(int x,int y){
	if(dep[x]>dep[y]) swap(x,y);
	for(int i=20;i>=0;i--){
		if(dep[f[y][i]]>=dep[x]) y=f[y][i];
	}
	if(x==y) return x;
	for(int i=20;i>=0;i--){
		if(f[x][i]!=f[y][i]){
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[y][0];
}
int query(int nl,int nr,int r,int l,int k,int lc,int lcaa){
	if(nl==nr) return nl;
	int mid=(nl+nr)>>1;
	int cn=tr[tr[r].l].sum+tr[tr[l].l].sum-tr[tr[lc].l].sum-tr[tr[lcaa].l].sum;
	if(cn>=k) return query(nl,mid,tr[r].l,tr[l].l,k,tr[lc].l,tr[lcaa].l);
	else return query(mid+1,nr,tr[r].r,tr[l].r,k-cn,tr[lc].r,tr[lcaa].r);
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	n=read(),q=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		lsh[i]=a[i];
	}
	sort(lsh+1,lsh+n+1);
	le=unique(lsh+1,lsh+n+1)-lsh-1;
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		tu[u].push_back(v);
		tu[v].push_back(u); 
	}
	rt[0]=build(1,le);
	dfs(1,0);
	int las=0;
	for(int i=1;i<=q;i++){
		int l=read(),r=read(),k=read();
		las^=l;
		int lc=lca(las,r);
		int p=query(1,le,rt[r],rt[las],k,rt[lc],rt[f[lc][0]]);
		cout<<lsh[p]<<'\n';
		las=lsh[p];
	}
	return 0;
}

T2 二维数点

主席树做法(强制在线):

把所有点一起离散化

对每个横坐标,记录每个纵坐标出现的次数

答案T x2 [y2-y1]- T x1-1 [y2-y1]

T3 标记永久化(区间加,访问历史区间和)

T4 Dynamic Rankings

树状数组套主席树

树状数组记录主席树根节点

T5 [SDOI2009] HH的项链

转化成二维数点

记录prei表示ai上次出现的位置

限制转化成要求 l<=i<=r且0<=prei<=l-1

发现和二维数点没区别

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define lson x<<1
#define rson x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct node{
	int sum;
	int l,r;
	node(){
		sum=l=r=0;
	}
}tr[30000005];
int cnt;
void update(int x){
	tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
}
int a[1000005],n,q,rt[1000005],pre[1000005],mmq[1000005];
int build(int l,int r){
	cnt++;
	int x=cnt;
	if(l==r){
		tr[x].sum=1;
		return x;
	}
	int mid=(l+r)>>1;
	tr[x].l=build(l,mid);
	tr[x].r=build(mid+1,r);
	update(x);
	return x;
}
int query(int l,int r,int x,int nl,int nr){
	if(nl<=l&&r<=nr) return tr[x].sum;
	int mid=(l+r)>>1;
	if(nl<=mid){
		if(mid<nr) return query(l,mid,tr[x].l,nl,nr)+query(mid+1,r,tr[x].r,nl,nr);
		else return query(l,mid,tr[x].l,nl,nr);
	}
	return query(mid+1,r,tr[x].r,nl,nr);
}
int modd(int l,int r,int x,int p,int v){
	cnt++;
	int pq=cnt;
	tr[pq]=tr[x];
	if(l==r){
		tr[pq].sum+=v;
		return pq;
	}
	int mid=(l+r)>>1;
	if(p<=mid) tr[pq].l=modd(l,mid,tr[pq].l,p,v);
	else tr[pq].r=modd(mid+1,r,tr[pq].r,p,v);
	update(pq);
	return pq;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	for(int i=1;i<=n;i++){
		pre[i]=mmq[a[i]];
		mmq[a[i]]=i;
	}
	rt[0]=build(0,n);
	for(int i=1;i<=n;i++){
		rt[i]=modd(0,n,rt[i-1],pre[i],1);
	}
	q=read();
	for(int i=1;i<=q;i++){
		int l=read(),r=read();
		int sum=query(0,n,rt[r],0,l-1)-query(0,n,rt[l-1],0,l-1);
		cout<<sum<<'\n';
	}
	return 0;
}

T6 Mishka and Interesting sum

答案=区间异或和^区间去重后的异或和

显然前一项可以前缀异或和进行计算

后一项可以转化成二维数点

记录prei表示ai上次出现的位置

限制转化成要求 l<=i<=r且0<=prei<=l-1

(和HH的项链一模一样)

无需离散化

注:n以内的若干个数随便选进行异或,和<=2*n

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define lson x<<1
#define rson x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct node{
	int sum;
	int l,r;
	node(){
		sum=l=r=0;
	}
}tr[50000005];
int cnt;
void update(int x){
	tr[x].sum=tr[tr[x].l].sum^tr[tr[x].r].sum;
}
int a[1000005],n,q,rt[1000005],pre[1000005],su[100005];
map<int,int> mmq;
int build(int l,int r){
	cnt++;
	int x=cnt;
	if(l==r){
		tr[x].sum=a[l];
		return x;
	}
	int mid=(l+r)>>1;
	tr[x].l=build(l,mid);
	tr[x].r=build(mid+1,r);
	update(x);
	return x;
}
int query(int l,int r,int x,int nl,int nr){
	if(nl<=l&&r<=nr) return tr[x].sum;
	int mid=(l+r)>>1;
	if(nl<=mid){
		if(mid<nr) return query(l,mid,tr[x].l,nl,nr)^query(mid+1,r,tr[x].r,nl,nr);
		else return query(l,mid,tr[x].l,nl,nr);
	}
	return query(mid+1,r,tr[x].r,nl,nr);
}
int modd(int l,int r,int x,int p,int v){
	cnt++;
	int pq=cnt;
	tr[pq]=tr[x];
	if(l==r){
		tr[pq].sum^=v;
		return pq;
	}
	int mid=(l+r)>>1;
	if(p<=mid) tr[pq].l=modd(l,mid,tr[pq].l,p,v);
	else tr[pq].r=modd(mid+1,r,tr[pq].r,p,v);
	update(pq);
	return pq;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		su[i]^=su[i-1];
		su[i]^=a[i];
	}
	for(int i=1;i<=n;i++){
		pre[i]=mmq[a[i]];
		mmq[a[i]]=i;
	}
	rt[0]=build(0,n);
	for(int i=1;i<=n;i++){
		rt[i]=modd(0,n,rt[i-1],pre[i],a[i]);
	}
	q=read();
	for(int i=1;i<=q;i++){
		int l=read(),r=read();
		int sum=query(0,n,rt[r],0,l-1)^query(0,n,rt[l-1],0,l-1);
		sum^=su[r];
		sum^=su[l-1];
		cout<<sum<<'\n';
	}
	return 0;
}

T7 [省选联考 2021 A/B 卷] 宝石

考虑u,v节点之间的路径是一条链

定义l为lca(u,v)

对于u~l倍增,假设跳到pk

对于l~v,考虑将从上往下跳转化成从下往上跳

二分答案x,考虑Px是否可跳到Pk且不超过l

可持久化数组记录Px向上跳最近跳到那个点+倍增

可持久化数组从fa转移

只有wu的点的信息变成u,其余不变

实现比较困难

超级倍增题

可持久化字典树

仿可持久化线段树

板子

高位贪心

建字典树

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,q;
int tr[20000005][2],en[20000005];
int a[600005],rt[600005],cnt;
void gai(int x,int k,int l,int r){
	if(k<0){
		en[r]=x;
		return ;
	}
	int kk=a[x]>>k&1;
	tr[r][kk]=++cnt;
	if(l) tr[r][kk^1]=tr[l][kk^1];
	gai(x,k-1,tr[l][kk],tr[r][kk]);
	en[r]=max(en[tr[r][0]],en[tr[r][1]]);
}
int query(int x,int k,int v,int dw){
	if(k<0) return a[en[x]]^v;
	int kk=v>>k&1;
	if(en[tr[x][kk^1]]>=dw) return query(tr[x][kk^1],k-1,v,dw);
	return query(tr[x][kk],k-1,v,dw);
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	n=read(),q=read();
	rt[0]=++cnt;
	en[0]=-1;	
	gai(0,23,-1,rt[0]);
	for(int i=1;i<=n;i++){
		a[i]=read();
		a[i]^=a[i-1];
		rt[i]=++cnt;
		gai(i,23,rt[i-1],rt[i]);
	}
	while(q--){
		char opt;
		cin>>opt;
		if(opt=='A'){
			int x=read();
			a[++n]=x;
			a[n]^=a[n-1];
			rt[n]=++cnt;
			gai(n,23,rt[n-1],rt[n]);
		}
		else{
			int l=read(),r=read();
			int x=read();
			cout<<query(rt[r-1],23,x^a[n],l-1)<<'\n';
		}
	}
	return 0;
}
posted @ 2025-08-11 11:33  gbrrain  阅读(10)  评论(0)    收藏  举报