Luogu P3834 【模板】可持久化线段树 1(主席树)
HU…终于理解了主席树…
关于主席树学习的几篇blog
主要突破时间:2018/5/30下午电脑课(昨天中午效率迷之低。。唉)
好的,现在我也有了模板了
#include<cstdio>
#include<cstring>
int a[2000010],b[2000100],root[2000100];
int len=0,n,m;
struct nod1{int l,r,c,lc,rc;}tr[4000100];
int dfs(int l,int r)
{
int x=l,y=r,m=b[(x+y)/2];
while(x<=y)
{
while(b[x]<m)x++;
while(b[y]>m)y--;
if(x<=y)
{
int t=b[x];b[x]=b[y];b[y]=t;
x++;y--;
}
}
if(l<y)dfs(l,y);
if(x<r)dfs(x,r);
}
int ef(int x)
{
int mid,ans;
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)/2;
if(b[mid]<=x)
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
return ans;
}
void bt(int &rt,int l,int r,int x)
{
//printf("1111");
if(rt==0)
{
len++;rt=len;
}
tr[rt].c++;
if(l==r)return ;
int mid=(l+r)/2;
if(x<=mid)bt(tr[rt].lc,l,mid,x);
else bt(tr[rt].rc,mid+1,r,x);
}
int add(int &x,int y)
{
if(x==0)
{
x=y;return 0;
}
if(y==0)return 0;
tr[x].c+=tr[y].c;
add(tr[x].lc,tr[y].lc);
add(tr[x].rc,tr[y].rc);
}
int find(int x,int y,int l,int r,int k)
{
if(l==r)return b[l];
int mid=(l+r)/2,g=tr[tr[x].lc].c-tr[tr[y].lc].c;
if(k<=g)return find(tr[x].lc,tr[y].lc,l,mid,k);
else return find(tr[x].rc,tr[y].rc,mid+1,r,k-g);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
dfs(1,n);//ok
/*for(int i=1;i<=n;i++)
{
printf("%d ",b[i]);
}printf("\n");*/
for(int i=1;i<=n;i++)
{
//printf("1111");
bt(root[i],1,n,ef(a[i]));//printf("11111");
add(root[i],root[i-1]);
}
for(int i=1;i<=m;i++)
{
int x,y,k;
scanf("%d %d %d",&x,&y,&k);
printf("%d\n",find(root[y],root[x-1],1,n,k));
}
}
注释版
#include<cstdio>
#include<cstring>
int a[2000010],b[2000100],root[2000100];
int len=0,n,m;
struct nod1{int l,r,c,lc,rc;}tr[4000100];
int dfs(int l,int r)
{
int x=l,y=r,m=b[(x+y)/2];
while(x<=y)
{
while(b[x]<m)x++;
while(b[y]>m)y--;
if(x<=y)
{
int t=b[x];b[x]=b[y];b[y]=t;
x++;y--;
}
}
if(l<y)dfs(l,y);
if(x<r)dfs(x,r);
}
int seat(int x)
{//x是该由哪个叶子结点管
int mid,ans;
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)/2;
if(b[mid]<=x)
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
return ans;
}
void update(int &rt,int l,int r,int x)
{
if(rt==0)
{
len++;rt=len;
}//少什么才建什么
tr[rt].c++;
if(l==r)return ;
int mid=(l+r)/2;
if(x<=mid)update(tr[rt].lc,l,mid,x);
else update(tr[rt].rc,mid+1,r,x);
//向左右儿子去
}
int mix(int &x,int y)
{
if(x==0)
//之前没有多建
{
x=y;return 0;
//那么说明没有少,就直接继承上一棵树的
}
if(y==0)return 0;
//没有被建,自然不会有左右儿子,不去了
tr[x].c+=tr[y].c;
mix(tr[x].lc,tr[y].lc);
mix(tr[x].rc,tr[y].rc);
}
int find(int x,int y,int l,int r,int k)
{
if(l==r)return b[l];
//找到了,返回该叶子节点掌管的值
int mid=(l+r)/2,g=tr[tr[x].lc].c-tr[tr[y].lc].c;
//相减就是要求范围内,这棵线段树,当前节点左儿子管的数量
if(k<=g)return find(tr[x].lc,tr[y].lc,l,mid,k);
//k如果小于,自然去左边找
else return find(tr[x].rc,tr[y].rc,mid+1,r,k-g);
//不然去右边找,记得要找k减去g后的排名才是在右儿子中要找的
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
dfs(1,n);
//b数组排了序!a没有
//b是下面格子的值,a不变,按a数组的顺序插入
for(int i=1;i<=n;i++)
{
update(root[i],1,n,seat(a[i]));
//每次插入以root[i]为根一条链,这条链维护i这个点的信息
mix(root[i],root[i-1]);
//将其与前i-1条链合并,这样root[i]为根的这条链维护的就是1~i的信息
}
for(int i=1;i<=m;i++)
{
int x,y,k;
scanf("%d %d %d",&x,&y,&k);
printf("%d\n",find(root[y],root[x-1],1,n,k));
}
}