恩接下来就是主席树
高级数据结构
然而还是只会敲模板啊啊啊啊
模板题
这个,无修改的,静态,主席树,模板题。看懂花了好长时间,会敲也是差不多看一眼敲一下的,再能调出来,那我还是很棒棒 呸
首先一个离散化,这个是很自然能想到的。因为要学主席树,首先要了解权值线段树这个概念。
权值线段树
按值域建树,打个比方:25 78 22 68 93 58 32(我随便乱打的啦♪(^∇^*)
那么这边根节点的区域范围就是:[1,93]
然后往下递归就跟线段树一样的
然后它权值线段树每个节点存的是值在当前区间范围内的数的个数。
好啦,现在了解了权值线段树了。主席树的做法就是,先把原数列中的每一个数都建一条链,加入到主席树中,然后还要连子节点。差不多就这样啦,反正我真的是说不清楚。
附上模板
#include<bits/stdc++.h>
using namespace std;
long long n,m,x,y,k,t,b[100010],f[100010];
struct tr
{
long long sum,l,r;
}
tr[10000010];
struct alsh
{
long long val,id,flag;
}
a[100010];
bool mycmp(alsh aa,alsh bb)
{
return aa.val<bb.val;
}
inline int read()
{
int num=0,flag=1;
char c=getchar();
for (;c<'0'||c>'9';c=getchar())
if (c=='-') flag=-1;
for (;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
return num*flag;
}
void build(long long &root,long long last,long long l,long long r,long long sum)
{
root=++t;
tr[root].sum=tr[last].sum+1;
tr[root].l=tr[last].l;
tr[root].r=tr[last].r;
if (l==r) return;
long long mid=(l+r)/2;
if (sum<=mid) build(tr[root].l,tr[last].l,l,mid,sum);
else build(tr[root].r,tr[last].r,mid+1,r,sum);
}
long long find(long long first,long long last,long long l,long long r,long long sum)
{
if (l==r) return l;
long long mid=(l+r)/2;
long long tot=tr[tr[last].l].sum-tr[tr[first].l].sum;
if (sum<=tot) return find(tr[first].l,tr[last].l,l,mid,sum);
else return find(tr[first].r,tr[last].r,mid+1,r,sum-tot);
}
int main()
{
n=read();
m=read();
for (int i=1;i<=n;++i)
{
a[i].val=read();
a[i].id=i;
}
sort(a+1,a+n+1,mycmp);
for (int i=1;i<=n;++i)
{
a[a[i].id].flag=++t;
b[t]=a[i].val;
while (a[i+1].val==a[i].val)
{
a[a[i+1].id].flag=t;
i++;
}
}
for (int i=1;i<=n;++i)
build(f[i],f[i-1],1,n,a[i].flag);
for (int i=1;i<=m;++i)
{
x=read();
y=read();
k=read();
printf("%lld\n",b[find(f[x-1],f[y],1,n,k)]);
}
return 0;
}
一道福利题
Q k l r 查询数列在第k个版本时,区间[l, r]上的最大值
M k p v 把数列在第k个版本时的第p个数修改为v,并产生一个新的数列版本
最开始会给你一个数列,作为第1个版本。
#include<bits/stdc++.h>
using namespace std;
long long n,q,k,x,y,t=1,temp=1,a[100010],f[100010];
struct tr
{
long long maxx,l,r;
}
tr[10000010];
inline int read()
{
int num=0,flag=1;
char c=getchar();
for (;c<'0'||c>'9';c=getchar())
if (c=='-') flag=-1;
for (;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
return num*flag;
}
void build(long long l,long long r,long long o)
{
if (l==r)
{
tr[o].maxx=a[l];
return;
}
long long mid=(l+r)/2;
tr[o].l=++t;
build(l,mid,t);
tr[o].r=++t;
build(mid+1,r,t);
tr[o].maxx=max(tr[tr[o].l].maxx,tr[tr[o].r].maxx);
}
void change(long long &root,long long last,long long l,long long r,long long sum)
{
root=++t;
tr[root].l=tr[last].l;
tr[root].r=tr[last].r;
if (l==r)
{
tr[root].maxx=y;
return;
}
long long mid=(l+r)/2;
if (sum<=mid) change(tr[root].l,tr[last].l,l,mid,sum);
else change(tr[root].r,tr[last].r,mid+1,r,sum);
tr[root].maxx=max(tr[tr[root].l].maxx,tr[tr[root].r].maxx);
}
long long find(long long l,long long r,long long o)
{
if (x>r||y<l) return -1;
if (x<=l&&y>=r) return tr[o].maxx;
long long mid=(l+r)/2;
long long tl=find(l,mid,tr[o].l);
long long trr=find(mid+1,r,tr[o].r);
return max(tl,trr);
}
int main()
{
n=read();
q=read();
for (int i=1;i<=n;++i)
a[i]=read();
build(1,n,1);
f[1]=1;
for (int i=1;i<=q;++i)
{
int d=read();
if (d==1)
{
k=read();
x=read();
y=read();
change(f[++temp],f[k],1,n,x);
}
if (d==0)
{
k=read();
x=read();
y=read();
printf("%lld\n",find(1,n,f[k]));
}
}
return 0;
}
恩这道题确实福利,既有线段树的思想又有主席树的思想,所以我觉得海星。