不帶修改主席樹模板
阿新 • • 發佈:2019-02-09
對於一部分線段樹看似無法直接做的題,可以用主席樹來做。
主席樹就是對每個字首開一棵線段樹,當然,直接這樣會MLE。
可以使用一種類似動態開節點的方法可以有效避免MLE。
具體可以參考我的部落格,那裡寫的更詳細一點:可持久化線段樹
因為主席樹是由字首加起來的,所以區間[l,r]的解可以類似字首和那樣a[r]-a[l-1]直接減
主席樹的詳細解答可以看看這裡
建樹
主程式
fo(i,1,n) {
root[i]=++tot;t[tot]=t[root[i-1]];
build(root[i],1,n,c[i]);
}
build
void build(int v,int i,int j,int z)
{
t[v].d++;if(i==j) return;
int m=(i+j)/2;
if(z<=m) t[++tot]=t[t[v].l],t[v].l=tot,build(t[v].l,i,m,z);
查詢請直接看下面的完整程式碼
模板題:不帶修改的區間K大(小)數查詢 poj2104
題目連結
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 101000
using namespace std;
int n,a[N],b[N],c[N],root[N],tot=0;
struct note{
int l,r,d;
};
note t[N*50];
bool cnt(int i,int j){return a[i]<a[j];}
void build(int v,int i,int j,int z)
{
t[v].d++;if(i==j) return;
int m=(i+j)/2;
if(z<=m) t[++tot]=t[t[v].l],t[v].l=tot,build(t[v].l,i,m,z);
else t[++tot]=t[t[v].r],t[v].r=tot,build(t[v].r,m+1,j,z);
}
int find(int v1,int v2,int i,int j,int k)
{
if(i==j) return i;
int jy=t[t[v2].l].d-t[t[v1].l].d,m=(i+j)/2;
if (jy>=k) return find(t[v1].l,t[v2].l,i,m,k);
else return find(t[v1].r,t[v2].r,m+1,j,k-jy);
}
int main()
{
int m;scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d",&a[i]),b[i]=i;
sort(b+1,b+n+1,cnt);
fo(i,1,n) c[b[i]]=i;
fo(i,1,n) {
root[i]=++tot;t[tot]=t[root[i-1]];
build(root[i],1,n,c[i]);
}
for(;m;m--)
{
int x,y,k;scanf("%d%d%d",&x,&y,&k);
printf("%d\n",a[b[find(root[x-1],root[y],1,n,k)]]);
}
}