线段树
线段树
暂时没想好原理,先贴个代码,留待后续更新………………(逃…………
线段树
建树
void build(int l,int r,int x){
tree[x].l=l;
tree[x].r=r;
tree[x].add=0;
if(l==r){
scanf("%lld",&tree[x].sum);
return ;
}
int tmp=x*2;
int mid=(l+r)/2;
build(l,mid,tmp);
build(mid+1,r,tmp+1);
pushup(x); //如果在建树的过程中给sum赋值,记得后面要pushup
}
区间更新
当然不止区间更新,还有一个单点更新,但是你可以把单点想成一个区间嘛,这样就变成了区间更新
void update(ll l,ll r,ll c,ll x){
if(r<tree[x].l||l>tree[x].r)
return;
if(l<=tree[x].l&&r>=tree[x].r){
tree[x].add+=c;
tree[x].sum+=c*(tree[x].r-tree[x].l+1);
return;
}
if(tree[x].add)
pushdown(x);
ll tmp=x*2;
update(l,r,c,tmp); // !!!
update(l,r,c,tmp+1);
pushup(x);
}
查询
ll query(ll l,ll r,ll x){
if(r<tree[x].l||l>tree[x].r) //要更新的区间不在该区间上
return 0;
if(l<=tree[x].l&&r>=tree[x].r){ //要更新区间包括了该区间
return tree[x].sum;
}
if(tree[x].add)
pushdown(x);
ll tmp=x*2;
ll mid=(tree[x].l+tree[x].r)/2;
if(r<=mid)
return query(l,r,tmp);
else if(l>mid)
return query(l,r,tmp+1);
else{
return query(l,mid,tmp)+query(mid+1,r,tmp+1);
}
}
完整代码
ll n,m;
ll ans;
struct Tree{
ll l,r;
ll sum,add;
}tree[MAXN*4];
void pushup(ll x){
ll tmp=2*x;
tree[x].sum=tree[tmp].sum+tree[tmp+1].sum;
}
void pushdown(ll x){
ll tmp=2*x;
tree[tmp].add+=tree[x].add;
tree[tmp+1].add+=tree[x].add;
tree[tmp].sum+=tree[x].add*(tree[tmp].r-tree[tmp].l+1);
tree[tmp+1].sum+=tree[x].add*(tree[tmp+1].r-tree[tmp+1].l+1);
tree[x].add=0;
}
//建树
void build(int l,int r,int x){
tree[x].l=l;
tree[x].r=r;
tree[x].add=0;
if(l==r){
scanf("%lld",&tree[x].sum);
return ;
}
int tmp=x*2;
int mid=(l+r)/2;
build(l,mid,tmp);
build(mid+1,r,tmp+1);
pushup(x); //如果在建树的过程中给sum赋值,记得后面要pushup
}
//区间更新
void update(ll l,ll r,ll c,ll x){
if(r<tree[x].l||l>tree[x].r)
return;
if(l<=tree[x].l&&r>=tree[x].r){
tree[x].add+=c;
tree[x].sum+=c*(tree[x].r-tree[x].l+1);
return;
}
if(tree[x].add)
pushdown(x);
ll tmp=x*2;
update(l,r,c,tmp); // !!!
update(l,r,c,tmp+1);
pushup(x);
}
//查询
ll query(ll l,ll r,ll x){
if(r<tree[x].l||l>tree[x].r) //要更新的区间不在该区间上
return 0;
if(l<=tree[x].l&&r>=tree[x].r){ //要更新区间包括了该区间
return tree[x].sum;
}
if(tree[x].add)
pushdown(x);
ll tmp=x*2;
ll mid=(tree[x].l+tree[x].r)/2;
if(r<=mid)
return query(l,r,tmp);
else if(l>mid)
return query(l,r,tmp+1);
else{
return query(l,mid,tmp)+query(mid+1,r,tmp+1);
}
}
主席树
主席树又叫可持久化线段树或者函数式线段树,其实都是同一种东西
以下代码已通过 洛谷P3834,你也可以去测试一下你自己的板子
更新
void add(int &now,int last,int l,int r,int x) {
now=++cut;
tr[now].ans=tr[last].ans+1;
tr[now].l=tr[last].l,tr[now].r=tr[last].r;
if(l==r) return ;
int mid=(l+r)>>1;
if(x<=mid) add(tr[now].l,tr[last].l,l,mid,x);
else add(tr[now].r,tr[last].r,mid+1,r,x);
return ;
}
查询
int query(int L,int R,int l,int r,int x) {
if(l==r) return l;
int p=tr[tr[R].l].ans-tr[tr[L].l].ans;
int mid=(l+r)>>1;
if(p>=x) return query(tr[L].l,tr[R].l,l,mid,x);
else return query(tr[L].r,tr[R].r,mid+1,r,x-p);
}
完整代码
#include <bits/stdc++.h>
#define MAXN 200001
using namespace std;
#define debug() cout<<"debugdebugdebug"<<endl
int root[MAXN],cut,a[MAXN],s[MAXN];
struct node {
int l,r,ans;
} tr[MAXN*20];
void add(int &now,int last,int l,int r,int x) {
now=++cut;
tr[now].ans=tr[last].ans+1;
tr[now].l=tr[last].l,tr[now].r=tr[last].r;
if(l==r) return ;
int mid=(l+r)>>1;
if(x<=mid) add(tr[now].l,tr[last].l,l,mid,x);
else add(tr[now].r,tr[last].r,mid+1,r,x);
return ;
}
int query(int L,int R,int l,int r,int x) {
if(l==r) return l;
int p=tr[tr[R].l].ans-tr[tr[L].l].ans;
int mid=(l+r)>>1;
if(p>=x) return query(tr[L].l,tr[R].l,l,mid,x);
else return query(tr[L].r,tr[R].r,mid+1,r,x-p);
}
int main() {
int n,m;
int x,y,z;
while(scanf("%d%d",&n,&m)!=EOF) {
cut=0;
memset(root,0,sizeof root);
for(int i=1; i<=n; i++){
cin>>s[i];
a[i]=s[i];
}
sort(s+1,s+n+1);
for(int i=1; i<=n; i++) {
int p=lower_bound(s+1,s+n+1,a[i])-s;
add(root[i],root[i-1],1,n,p);
}
while(m--) {
cin>>x>>y>>z;
int p=query(root[x-1],root[y],1,n,z);
printf("%d\n",s[p]);
}
}
return 0;
}