可持久化数组 (luogu3919)

本文介绍了一种使用主席树实现的可持久化数组,支持单点查询和修改历史版本的状态值。通过构建普通线段树,每个版本的数组状态得以保存,允许高效查询任意历史版本中特定位置的值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

可持久化数组支持单点查询某个状态的值 , 单点修改某个状态的值

具体实现如下 :

建一棵主席树 (不同于权值线段树 , 是普通的线段树)

最后一层的节点的值 , 就是当前版本那个点的值

于是查询一个版本某个节点的值, 查对应子树对应的值就可以了

#include<bits/stdc++.h>
#define N 1000050
using namespace std;

struct Node{
	int ls,rs,val;
}t[N*20];

int n,m,a[N],cnt;
int rt[N];

int read(){
	int cnt=0,f=1; char ch=0;
	while(!isdigit(ch)){ch=getchar(); if(ch=='-') f=-1;}
	while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
	return cnt*f;
}
void Build(int &x,int l,int r){
	x = ++cnt;
	if(l==r){ t[x].val = a[l]; return;}
	int mid = (l+r)>>1;
	Build(t[x].ls, l, mid);
	Build(t[x].rs, mid+1, r);
}
void Update(int &x, int last, int L, int R,int pos,int val){
	x = ++cnt;  t[x] = t[last];
	if(L==R){ t[x].val = val; return;}
	int mid = (L+R) >> 1;
	if(pos<=mid) Update(t[x].ls, t[last].ls, L, mid, pos, val);
	else Update(t[x].rs, t[last].rs, mid+1, R, pos, val);
}
int Quary(int x,int L,int R,int pos){
	if(L==R) return t[x].val;
	int mid = (L+R) >> 1;
	if(pos<=mid) return Quary(t[x].ls, L, mid, pos);
	else return Quary(t[x].rs, mid+1, R, pos);
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) a[i] = read();
	Build(rt[0],1,n);
	for(int i=1;i<=m;i++){
		int v=read(), op=read(), x=read();
		if(op==1){
			int val = read();
			Update(rt[i], rt[v], 1, n, x, val);
			// 在版本v的基础上, 建立第i个版本 
		}
		if(op==2){
			int ans = Quary(rt[v], 1, n, x);
			printf("%d\n",ans);  rt[i] = rt[v];
		}
	}  return 0;
}

 

``` #include <bits/stdc++.h> #define ll long long #define lb(x) x&(-x) using namespace std; const ll N=1e5,M=5e5,TR=2e7; struct node{ ll ls,rs,w,val; }tr[TR+5]; ll n,m; ll a[N+5]; vector<ll> v[M+5]; ll b[M+5],cntb; ll root[M+5],cnttr; ll trr[N+5]; ll minn; void modify(ll x,ll y){ if(!x) return; for(ll i=x;i<=n;i+=lb(i)){ trr[i]+=y; } return; } ll query(ll x){ ll s=0; for(ll i=x;i;i-=lb(i)){ s+=trr[i]; } return s; } ll query(ll l,ll r){ return query(r)-query(l-1); } void split(ll p,ll v,ll &x,ll &y){ if(!p){ x=y=0; return; } if(tr[p].val<=v){ x=p; split(tr[p].rs,v,tr[p].rs,y); } else{ y=p; split(tr[p].ls,v,x,tr[p].ls); } return; } void merge(ll x,ll y,ll &p){ if(!x || !y){ p=x+y; return; } if(tr[x].w>=tr[y].w){ p=x; merge(tr[p].rs,y,tr[p].rs); } else{ p=y; merge(x,tr[p].ls,tr[p].ls); } return; } void build(ll &p,ll l,ll r){ if(l>r) return; ll mid=(l+r)>>1; p=++cnttr; tr[p]=(node){0,0,rand(),b[mid]}; build(tr[cnttr].ls,l,mid-1); build(tr[cnttr].rs,mid+1,r); return; } void dfs(ll p,ll x){ if(!p) return; minn=min(minn,p); dfs(tr[p].ls,x); ll pos=tr[p].val; modify(pos,-a[pos]+a[pos]/x); a[pos]/=x; if(a[pos]%x==0) b[++cntb]=pos; dfs(tr[p].rs,x); return; } void work(ll p){ if(!p) return; work(tr[p].ls); cout<<p<<" "<<tr[p].ls<<" "<<tr[p].rs<<" "<<tr[p].val<<endl; work(tr[p].rs); return; } int main(){ srand(time(NULL)); // srand(5); scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++){ scanf("%lld",&a[i]); for(ll j=1;j*j<=a[i];j++){ if(i%j==0){ v[j].push_back(i); if(j*j!=a[i]) v[i/j].push_back(i); } } modify(i,a[i]); } for(ll i=1;i<=N;i++){ cntb=0; // cout<<i<<"ooooooo\n"; for(ll j:v[i]){ b[++cntb]=j; // cout<<j<<" "; } // cout<<endl; build(root[i],1,cntb); } while(m--){ ll op,l,r,v; scanf("%lld%lld%lld",&op,&l,&r); if(op==1){ scanf("%lld",&v); if(v<=1) continue; ll x=0,y=0,z=0; split(root[v],r,x,z); split(x,l-1,x,y); // work(y); minn=TR; cntb=0; dfs(y,v); // cnttr=minn-1; build(y,1,cntb); merge(x,y,x); merge(x,z,root[v]); } else printf("%lld\n",query(l,r)); } return 0; } /* 5 1 1 2 3 4 5 1 1 5 2 */ ``` # P3987 我永远喜欢珂朵莉~ ## 题目背景 http://sukasuka-anime.com/ 戒不掉的珂毒 出不动的分块 ![](https://cdn.luogu.com.cn/upload/pic/11191.png) ![](https://cdn.luogu.com.cn/upload/pic/11192.png) ![](https://cdn.luogu.com.cn/upload/pic/11193.png) 哦对了有没有想买BD的珂学家啊?支持一下墨鱼吧~ 或者有没有人想来手办众筹啊? ## 题目描述 给珂朵莉一个长为 $n$ 的非负数序列 $a$,支持以下两个操作: - $\verb!1 l r x!$:把区间 $[l,r]$ 中所有 $x$ 的倍数除以 $x$。 - $\verb!2 l r!$:查询区间 $[l,r]$ 内元素的和。 珂朵莉很可爱,所以你要帮珂朵莉写这个题。 ## 输入格式 第一行两个数表示 $n,m$。 第二行 $n$ 个非负整数表示 $a_i$。 之后 $m$ 行每行一个操作: - $\verb!1 l r x!$:把区间 $[l,r]$ 中所有 $x$ 的倍数除以 $x$。 - $\verb!2 l r!$:查询区间 $[l,r]$ 内元素的和。 ## 输出格式 对于每次询问,输出一行一个数表示答案。 ## 输入输出样例 #1 ### 输入 #1 ``` 5 3 1 2 3 4 5 2 1 5 1 1 5 2 2 1 5 ``` ### 输出 #1 ``` 15 12 ``` ## 说明/提示 ### 数据范围及约定 $1 \le n , m \le 10^5$,$0 \le a_i \le 5\times 10^5$,$1 \le x \le 5\times 10^5$。 上面代码过不了样例
最新发布
08-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值