可持久化数据结构
今天学了可持久化,于是稍微写写
可持久化线段树(主席树)
众所周知:主席树里没有主席
我们就是在维护许多历史版本,但是对于每个历史版本都暴力建树的话,时空都不允许。所以我们只针对变化的节点进行新建即可,并存起每个历史版本的根节点。
可持久化数组
相比于普通的线段树,就是多了对历史版本的操作,根据上面的思想,我们可以写出 \(update\):
$update$
int clone(int rt)
{
v[++segcnt]=v[rt];
return segcnt;
}
void update(int &rt,int l,int r,int pos,int val)
{
rt=clone(rt);
if(l==r) return v[rt].val=val,void();
int mid=(l+r)>>1;
if(pos<=mid) update(lson,l,mid,pos,val);
else update(rson,mid+1,r,pos,val);
}
静态区间第K小
但主席树不止到此为止,如题:
对区间的询问仍可以用主席树解决。
考虑这样的方法:扫描序列,维护可持久化权值线段树(并记录权值出现次数),对每个位置维护一个历史版本(这个版本有他及他前面的信息)。当询问区间时,区间权值出现次数就是右端点维护的线段树的次数减去(左端点-1)维护的线段树的次数,线段树上二分即可找到第k小,由此引入一个思想:前缀主席树。
code
for(int i=1,op;i<=n;i++)
{
cin>>op;
root[i]=root[i-1];
update(root[i],-1e9,1e9,op);
}
int query(int x,int y,int k,int l,int r)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(k<=v[v[y].ls].cnt-v[v[x].ls].cnt) return query(v[x].ls,v[y].ls,k,l,mid);
else return query(v[x].rs,v[y].rs,k-(v[v[y].ls].cnt-v[v[x].ls].cnt),mid+1,r);
}
同理我不仅可以对每个位置维护历史版本,还可以将权值排序,有小到大维护每个历史版本。询问时,进行二分,查询对应线段树的区间,若 \(cnt<=k\) 向右二分,否则向左二分。
p2633 count on a tree
利用上面的思想我们便可以做出下面这道题:
p2633 count on a tree
用前缀主席树思想,对每个节点在父亲维护的历史版本上进行可持久化更新,最后统计答案利用树上查分:\(cnt_x+cnt_y-cnt_{lca(x,y)}-cnt_{fa(lca)}\)即可
code
#include<iostream>
#include<string.h>
#include<vector>
#include<iomanip>
#include<algorithm>
#define lson v[rt].ls
#define rson v[rt].rs
using namespace std;
const int maxn=1e5+5;
int n,m,a[maxn],last,root[maxn],segcnt;
vector<int> e[maxn];
int st[maxn][18],d[maxn];
int df[maxn],cnt;
struct nd
{
int ls,rs,cnt;
}v[maxn*30];
int clone(int rt)
{
v[++segcnt]=v[rt];
return segcnt;
}
void pushup(int rt)
{
v[rt].cnt=v[lson].cnt+v[rson].cnt;
}
void update(int &rt,int l,int r,int pos)
{
rt=clone(rt);
if(l==r) return v[rt].cnt++,void();
int mid=(l+r)>>1;
if(pos<=mid) update(lson,l,mid,pos);
else update(rson,mid+1,r,pos);
pushup(rt);
}
void dfs(int x,int fa)
{
root[x]=root[fa];
update(root[x],1,cnt,a[x]);
for(int i:e[x])
{
if(i==fa) continue;
d[i]=d[x]+1;
st[i][0]=x;
for(int j=1;j<=17;j++)
{
st[i][j]=st[st[i][j-1]][j-1];
}
dfs(i,x);
}
}
int lca(int x,int y)
{
if(d[x]<d[y]) swap(x,y);
for(int i=17;i>=0;i--)
{
if(st[x][i]==0) continue;
if(d[st[x][i]]>=d[y]) x=st[x][i];
}
if(x==y) return x;
for(int i=17;i>=0;i--)
{
if(st[y][i]!=st[x][i])
{
x=st[x][i],y=st[y][i];
}
}
return st[y][0];
}
int query(int x,int y,int z,int q,int k,int l,int r)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(k<=v[v[x].ls].cnt+v[v[y].ls].cnt-v[v[z].ls].cnt-v[v[q].ls].cnt)
return query(v[x].ls,v[y].ls,v[z].ls,v[q].ls,k,l,mid);
else
{
int op=k-v[v[x].ls].cnt-v[v[y].ls].cnt+v[v[z].ls].cnt+v[v[q].ls].cnt;
return query(v[x].rs,v[y].rs,v[z].rs,v[q].rs,op,mid+1,r);
}
}
signed main()
{
ios::sync_with_stdio(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>df[i];a[i]=df[i];
}
sort(df+1,df+1+n);
cnt=unique(df+1,df+1+n)-df-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(df+1,df+1+cnt,a[i])-df;
for(int i=1,l,r;i<n;i++)
{
cin>>l>>r;
e[l].push_back(r);
e[r].push_back(l);
}
d[1]=1;
dfs(1,0);
for(int i=1;i<=m;i++)
{
int l,r,k;
cin>>l>>r>>k;
l^=last;
int lcc=lca(l,r);
last=df[query(root[l],root[r],root[lcc],root[st[lcc][0]],k,1,cnt)];
cout<<last<<'\n';
}
return 0;
}
/**/
【国家集训队】middle
接着我们便可以进阶:
我们发现找区间中位数,实际上可以等价成查找一个数,使它在区间中小于它的数等于大于它的数。
当然因为若区间长为偶数,取靠右边的那一个。所以找一个最大的数,使小于他的数比大于等于它的数小。
显然答案具有单调性,数越大越不易满足条件。
所以我们进行二分,对于每一个\(mid\)将区间中小于他的数设为\(-1\),否则设为\(1\),若区间和不小于零,则可以向更大的值推进。
同时我们发现这个答案一定是序列的数(废话),所以\(mid\)只用取序列的数即可,这样每次二分的正负\(1\)序列便可以预处理出来。
又因为使答案最大,区间左右端点不定,所以为了使区间和最大,就要用到最大子段和,线段树就推出来了。又要维护多个不同序列,主席树便显然了。
具体维护过程:
- 对于序列按照值排序
- 扫描排序后的序列
- 最开始树上都是\(1\)
- 扫描到一个值,将他前一个值对应原序列的位置,在主席树上改为\(-1\)
- 二分序列的值,在这个值所对应的主席树上求区间和即可
预处理过程可能不大清楚,结合代码理解一下
code
#include<iostream>
#include<string.h>
#include<vector>
#include<iomanip>
#include<algorithm>
#include<queue>
#define lson v[rt].ls
#define rson v[rt].rs
using namespace std;
#define int long long
const int maxn=2e4+5;
int root[maxn],segcnt;
pair<int,int> a[maxn];
int n,m;
struct nd
{
int ls,rs,val,lsval,rsval;
}v[maxn*30];
int clone(int rt)
{
v[++segcnt]=v[rt];
return segcnt;
}
void pushup(int rt)
{
v[rt].val=v[lson].val+v[rson].val;
v[rt].lsval=max(v[lson].lsval,v[lson].val+v[rson].lsval);
v[rt].rsval=max(v[rson].rsval,v[lson].rsval+v[rson].val);
}
void build(int &rt,int l,int r)
{
rt=clone(rt);
if(l==r)
{
v[rt].lsval=v[rt].rsval=v[rt].val=1;
return;
}
int mid=(l+r)>>1;
build(lson,l,mid);build(rson,mid+1,r);
pushup(rt);
}
void update(int &rt,int l,int r,int pos)
{
rt=clone(rt);
if(l==r)
{
v[rt].lsval=v[rt].rsval=v[rt].val=-1;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) update(lson,l,mid,pos);
else update(rson,mid+1,r,pos);
pushup(rt);
}
int queryval(int rt,int l,int r,int L,int R)
{
if(L>R) return 0;
if(l>=L&&r<=R) return v[rt].val;
int mid=(l+r)>>1;
int ret=0;
if(L<=mid) ret+=queryval(lson,l,mid,L,R);
if(R>mid) ret+=queryval(rson,mid+1,r,L,R);
return ret;
}
pair<int,int> querylsval(int rt,int l,int r,int L,int R)
{
if(l>=L&&r<=R) return {v[rt].lsval,v[rt].val};
int mid=(l+r)>>1;
if(R<=mid) return querylsval(lson,l,mid,L,R);
else if(L>mid) return querylsval(rson,mid+1,r,L,R);
else
{
auto x=querylsval(lson,l,mid,L,R),y=querylsval(rson,mid+1,r,L,R);
pair<int,int> z;
z.second=x.second+y.second;
z.first=max(x.first,x.second+y.first);
return z;
}
}
pair<int,int> queryrsval(int rt,int l,int r,int L,int R)
{
if(l>=L&&r<=R) return {v[rt].rsval,v[rt].val};
int mid=(l+r)>>1;
if(R<=mid) return queryrsval(lson,l,mid,L,R);
else if(L>mid) return queryrsval(rson,mid+1,r,L,R);
else
{
auto x=queryrsval(lson,l,mid,L,R),y=queryrsval(rson,mid+1,r,L,R);
pair<int,int> z;
z.second=x.second+y.second;
z.first=max(y.first,x.first+y.second);
return z;
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].first;
a[i].second=i;
}
sort(a+1,a+1+n);
build(root[1],1,n);
for(int i=2;i<=n;i++)
{
root[i]=root[i-1];
update(root[i],1,n,a[i-1].second);
}
cin>>m;
int last=0;
for(int i=1;i<=m;i++)
{
int x,y,z,q;
int c[5]={};
for(int j=1;j<=4;j++) cin>>c[j];
for(int j=1;j<=4;j++) c[j]=(c[j]+last)%n+1;
sort(c+1,c+1+4);
x=c[1],y=c[2],z=c[3],q=c[4];
int l=1,r=n,ret=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(queryval(root[mid],1,n,y+1,z-1)+querylsval(root[mid],1,n,z,q).first+queryrsval(root[mid],1,n,x,y).first>=0)
{
ret=a[mid].first;
l=mid+1;
}
else r=mid-1;
}
last=ret;
cout<<ret<<'\n';
}
return 0;
}

浙公网安备 33010602011771号