一:静态无修改
经典的方法就是使用划分树。。不过划分树写起来有点纠结。。。(时代的眼泪啊)
现在这个问题一般都是使用主席树来解决,其思想是这样的:对于原序列每一个前缀,都在其上建立一颗以值为轴的线段树(当然要离散化),保存区间内的值出现的次数,当查询一段区间最小值的时候,相当于在 右端点的线段树减去左端点的线段树 所得到的线段树上找第k个有值的点。。。(注意:权值线段树是可做差的,其差线段树的每个节点值相等于两颗原线段树节点值的差。。。所以并不用保存所得线段树)
但是如果直接开的话。。。空间复杂度4*N*N...太大了。。。
考虑到每一个线段树比上一个线段树有不同的节点只有 当前加进来的值所在的区间节点。。。一共只有logn个。。所以除了这些节点。。。其他节点都用上一颗线段树的就好了。。。。于是传统的线段树数组建树的方法就不好用了。。。就用指针来建
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,m;
int a[100000+10];//原数组
int b[100000+10];//排序离散数组
int N;
struct node
{
int sum;
node *l,*r;
}tree[100000*24+10],*root[100000+10];
node *newnode()
{
tree[N].l=tree[N].r=NULL;
tree[N].sum=0;
return &tree[N++];
}
node *newnode(node *x)
{
tree[N]=*x;
return &tree[N++];
}
node *build(int l,int r)
{
node *x=newnode();
if(l==r)return x;
else
{
int mid=l+r>>1;
x->l=build(l,mid);
x->r=build(mid+1,r);
x->sum=x->l->sum+x->r->sum;
return x;
}
}
node *update(node *last,int tl,int tr,int pos,int val)
{
node *x=newnode(last);
if(tl==tr)
{
x->sum+=val;
return x;
}
else
{
int mid=tl+tr>>1;
if(pos<=mid)x->l=update(last->l,tl,mid,pos,val);
else x->r=update(last->r,mid+1,tr,pos,val);
x->sum=x->l->sum+x->r->sum;
return x;
}
}
int query(node *n1,node *n2,int tl,int tr,int k)
{
if(tl==tr)return tl;
else
{
int mid=tl+tr>>1;
int lsum=n1->l->sum-n2->l->sum;
if(lsum>=k)return query(n1->l,n2->l,tl,mid,k);
else return query(n1->r,n2->r,mid+1,tr,k-lsum);
}
}
int inti()
{
memcpy(b,a,sizeof(a));
sort(b+1,b+n+1);
int nt=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;i++)
{
int l=1,r=nt;
while(l<r)
{
int mid=l+r>>1;
if(a[i]<=b[mid])r=mid;
else l=mid+1;
}
a[i]=l;
}
return nt;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int tn=inti();//离散并得到离散后元素个数
N=0;
root[0]=build(1,tn);
for(int i=1;i<=n;i++)root[i]=update(root[i-1],1,tn,a[i],1);
for(int i=1;i<=m;i++)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",b[query(root[r],root[l-1],1,tn,k)]);
}
return 0;
}
二.动态点更新的区间第K大问题
这个嘛其实也可以用主席树解决。。。但是我还没学会。
今天早上学了下线段树套平衡树的方法:现在原序列上建一颗线段树,对每个线段树节点建立一颗平衡树来储存这个区间内所有的点。。。
更新时,把每个包含当前点的区间(logn个)节点上的平衡树中删掉更新前的值,加入更新后的值。。。
然后查询是要这样:二分答案的值。。。然后查询此区间包含的每个整区间中小于等于二分值的个数。。。用这个值来与k比较,然后缩小答案范围。。。最后出解
我平衡树写的是splay。。。因为目前我只会这个,但是速度很不理想,每个点都要超时限3秒多。。。
以后再来改进下
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=100000;
int s[MAXN+10];
struct Node
{
int sz,val;
Node *fa,*c[2];
};
struct SPLAY
{
Node *nil,*root;
Node *newnode(int val,Node *fa)
{
Node *x=new Node;
x->fa=fa;
x->val=val;
x->sz=1;
x->c[0]=x->c[1]=nil;
return x;
}
void inti(int val)
{
nil=newnode(0,nil);//val,fa
nil->fa=nil->c[0]=nil->c[1]=nil;
nil->sz=nil->val=0;
root=newnode(val,nil);
}
void up(Node *x)
{
x->sz=x->c[0]->sz+x->c[1]->sz+1;
}
void rotate(Node *x,int d)
{
Node *y=x->fa;
if(y->fa!=nil)
{
if(y->fa->c[0]==y)y->fa->c[0]=x;
else y->fa->c[1]==x;
}
x->fa=y->fa;
y->fa=x;
if(x->c[d^1]!=nil)x->c[d^1]->fa=y;
y->c[d]=x->c[d^1];
x->c[d^1]=y;
up(y);
}
void splay(Node *x,Node *fa)
{
while(x->fa!=fa)
{
if(x->fa->fa==fa)
{
if(x->fa->c[0]==x)rotate(x,0);//右旋
else rotate(x,1);//左旋
}
else
{
Node *y=x->fa;
if(y->fa->c[0]==y)
{
if(x->fa->c[0]==x)
{
rotate(y,0);
rotate(x,0);
}
else
{
rotate(x,1);
rotate(x,0);
}
}
else
{
if(x->fa->c[0]==x)
{
rotate(x,0);
rotate(x,1);
}
else
{
rotate(y,1);
rotate(x,1);
}
}
}
}
up(x);
if(x->fa==nil)root=x;
}
void select(int k,Node *fa)
{
Node *x=root;
while(x!=nil)
{
int tem=x->c[0]->sz+1;
if(tem==k)break;
else if(tem>k)x=x->c[0];
else if(tem<k)
{
k-=tem;
x=x->c[1];
}
}
splay(x,fa);
}
void insert(int val)
{
if(root==nil)
{
root=newnode(val,nil);
return;
}
Node *x=root;
while(1)
{
if(val>x->val)
{
if(x->c[1]==nil)break;
x=x->c[1];
}
else
{
if(x->c[0]==nil)break;
x=x->c[0];
}
}
if(val>x->val)
{
x->c[1]=newnode(val,x);
x=x->c[1];
}
else
{
x->c[0]=newnode(val,x);
x=x->c[0];
}
splay(x,nil);
}
void delet(int val)
{
Node *x=root;
while(x!=nil)
{
if(x->val<val)
{
x=x->c[1];
}
else if(x->val>val)
{
x=x->c[0];
}
else
{
break;
}
}
splay(x,nil);
if(root->c[0]!=nil)
{
select(root->c[0]->sz,root);
root->c[0]->c[1]=root->c[1];
if(root->c[1]!=nil)root->c[1]->fa=root->c[0];
root->c[0]->fa=nil;
Node *lroot=root;
root=root->c[0];
delete lroot;
up(root);
}
else
{
root->c[1]->fa=nil;
Node *lroot=root;
root=root->c[1];
delete lroot;
}
}
int _query(Node *x,int mid)
{
if(x==nil)return 0;
if(mid<x->val)return _query(x->c[0],mid);
else return x->c[0]->sz+1+_query(x->c[1],mid);
}
int num(int mid)
{
// int x=((root->val)%(root->sz)+(root->sz))%(root->sz)+1;
// select(x,nil);
return _query(root,mid);
}
};
int n,m;
struct T
{
SPLAY splay;
}tree[MAXN*4+10];
void build(int id,int l,int r)
{
tree[id].splay.inti(s[l]);
for(int i=l+1;i<=r;i++)tree[id].splay.insert(s[i]);
if(l==r)return;
else
{
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
}
}
int query(int id,int tl,int tr,int l,int r,int mid)
{
if(l<=tl&&tr<=r)
{
return tree[id].splay.num(mid);
}
else
{
int mid1=(tl+tr)>>1;
int ret=0;
if(l<=mid1)ret+=query(id<<1,tl,mid1,l,r,mid);
if(r>mid1)ret+=query(id<<1|1,mid1+1,tr,l,r,mid);
return ret;
}
}
void update(int id,int tl,int tr,int pos,int val)
{
tree[id].splay.delet(s[pos]);
tree[id].splay.insert(val);
if(tl==tr)return;
else
{
int mid=(tl+tr)>>1;
if(pos<=mid)update(id<<1,tl,mid,pos,val);
else update(id<<1|1,mid+1,tr,pos,val);
}
}
int main()
{
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&s[i]);
build(1,1,n);
for(int i=1;i<=m;i++)
{
int K;
scanf("%d",&K);
if(K==1)
{
int a,b,k;
scanf("%d%d%d",&a,&b,&k);
int l=-n,r=n;
k=b-a+2-k;
while(l<r)
{
int mid=(l+r)>>1;
if(query(1,1,n,a,b,mid)>=k)r=mid;
else l=mid+1;
}
printf("%d\n",l);
}
else if(K==2)
{
int p,x;
scanf("%d%d",&p,&x);
update(1,1,n,p,x);
s[p]=x;
}
}
return 0;
}