主席樹模板總結及題集
介紹篇:http://blog.csdn.net/metalseed/article/details/8045038 ->
理解篇(配圖比較生動):http://blog.csdn.net/regina8023/article/details/41910615 ->
主席樹三個經常用到的點:
1.查詢區間第k大(線上)
2.查詢區間第k大且有更新節點操作(離線,離線主要是為了將線段樹上的值離散化,如果數值範圍小則不需要離散化,也不需要離線,另外更新操作需要套樹狀陣列)
3.查詢區間不同數的個數(樹狀陣列可以實現,但只能離線,主席樹實現可以線上)
還有很多線段樹能實現的,加了一些條件,都可以用主席樹來解。
下面看題:
區間查詢第k大元素,非遞迴寫法,非遞迴不好進行回溯操作,遞迴更好寫一些
unique函式:在STL中unique函式是一個去重函式, unique的功能是去除相鄰的重複元素(只保留一個),其實它並不真正把重複的元素刪除,是把重複的元素移到後面去了,然後依然儲存到了原陣列中,然後 返回去重後最後一個元素的地址,因為unique去除的是相鄰的重複元素,所以一般用之前都會要排一下序。#include<iostream> #include<stdio.h> #include<algorithm> using namespace std; const int maxn=100005; int n,m; int v[maxn],b[maxn],t[maxn],tsize,tot=0; struct charitree { int l,r,tsize; }a[maxn*30]; int build(int l,int r) { int root=tot++; a[root].tsize=0; if(l==r) return root; int m=(l+r)>>1; a[root].l=build(l,m); a[root].r=build(m+1,r); return root; } int update(int root,int x) { int now=tot++; int tmp=now; a[now].tsize=a[root].tsize+1; int l=1,r=tsize; while(l<r) { int m=(l+r)>>1; if(x<=m) { a[now].l=tot++; a[now].r=a[root].r; now=a[now].l; r=m; root=a[root].l; } else { a[now].l=a[root].l; a[now].r=tot++; now=a[now].r; l=m+1; root=a[root].r; } a[now].tsize=a[root].tsize+1; } return tmp; } int ask(int lx,int rx,int k) { int l=1,r=tsize; while(l<r) { int m=(l+r)>>1; if(a[a[rx].l].tsize-a[a[lx].l].tsize>=k) { r=m; lx=a[lx].l; rx=a[rx].l; } else { l=m+1; k-=a[a[rx].l].tsize-a[a[lx].l].tsize; lx=a[lx].r; rx=a[rx].r; } } return l; } void hash1() { sort(b+1,b+1+n); tsize=unique(b+1,b+1+n)-b-1;//去重函式 } int find_index(int x) { return lower_bound(b+1,b+1+tsize,x)-b; } int main() { int i,j,l,r,k; cin>>n>>m; for(i=1;i<=n;i++) scanf("%d",&v[i]),b[i]=v[i]; hash1(); t[0]=build(1,tsize); for(i=1;i<=n;i++) t[i]=update(t[i-1],find_index(v[i])); while(m--) { cin>>l>>r>>k; printf("%d\n",b[ask(t[l-1],t[r],k)]); } return 0; }
區間查詢小於等於k的數的個數,和上題基本類似,修改了一下查詢的函式
#include<iostream> #include<stdio.h> #include<algorithm> using namespace std; const int maxn=100005; int n,m; int v[maxn],b[maxn],t[maxn],tsize,tot=0; struct charitree { int l,r,tsize; }a[maxn*30]; int build(int l,int r) { int root=tot++; a[root].tsize=0; if(l==r) return root; int m=(l+r)>>1; a[root].l=build(l,m); a[root].r=build(m+1,r); return root; } int update(int root,int x) { int now=tot++; int tmp=now; a[now].tsize=a[root].tsize+1; int l=1,r=tsize; while(l<r) { int m=(l+r)>>1; if(x<=m) { a[now].l=tot++; a[now].r=a[root].r; now=a[now].l; r=m; root=a[root].l; } else { a[now].l=a[root].l; a[now].r=tot++; now=a[now].r; l=m+1; root=a[root].r; } a[now].tsize=a[root].tsize+1; } return tmp; } int ask(int lx,int rx,int le,int ri,int k) { if(b[ri]<=k) { return a[rx].tsize - a[lx].tsize; } if(le==ri) return 0; int sum=0; int m=(le + ri) >> 1; if(b[m+1]<=k) sum+=ask(a[lx].r,a[rx].r,m+1,ri,k); sum+=ask(a[lx].l,a[rx].l,le,m,k); return sum; } void hash1() { sort(b+1,b+1+n); tsize=unique(b+1,b+1+n)-b-1; } int find_index(int x) { return lower_bound(b+1,b+1+tsize,x)-b; } int main() { int T; int i,j,l,r,k; cin>>T; for(int tt = 1; tt <= T; tt++) { cin>>n>>m; for(i=1;i<=n;i++) scanf("%d",&v[i]),b[i]=v[i]; hash1(); t[0]=build(1,tsize); for(i=1;i<=n;i++) t[i]=update(t[i-1],find_index(v[i])); printf("Case %d:\n",tt); while(m--) { cin>>l>>r>>k; l++,r++; printf("%d\n",ask(t[l-1],t[r],1,tsize,k)); } } return 0; }
題意:給一顆樹,每個節點都有一個權值,查詢兩個節點之間路徑上的權值第k小節點
主席樹,和上題只是線性的建樹和樹上建樹的區別,每一個節點由其父節點修改而來,兩個節點的路徑lca求一求就可以。僅是查詢,所以可以線上
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
const int maxn=100005;
int n,m;
int v[maxn],b[maxn],t[maxn],tsize,tot=0;
struct charitree
{
int l,r,tsize;
}a[maxn*30];
vector<int>w[maxn];
int book[maxn],fa[maxn],dep[maxn],up[maxn][20];
int build(int l,int r)
{
int root=tot++;
a[root].tsize=0;
if(l==r)
return root;
int m=(l+r)>>1;
a[root].l=build(l,m);
a[root].r=build(m+1,r);
return root;
}
int update(int root,int x)
{
int now=tot++;
int tmp=now;
a[now].tsize=a[root].tsize+1;
int l=1,r=tsize;
while(l<r)
{
int m=(l+r)>>1;
if(x<=m)
{
a[now].l=tot++;
a[now].r=a[root].r;
now=a[now].l;
r=m;
root=a[root].l;
}
else
{
a[now].l=a[root].l;
a[now].r=tot++;
now=a[now].r;
l=m+1;
root=a[root].r;
}
a[now].tsize=a[root].tsize+1;
}
return tmp;
}
void hash1()
{
sort(b+1,b+1+n);
tsize=unique(b+1,b+1+n)-b-1;
}
int find_index(int x)
{
return lower_bound(b+1,b+1+tsize,x)-b;
}
int init()
{
memset(up,0,sizeof(up));
for(int i=1;i<=n;i++)
up[i][0]=fa[i];
for(int j=1;j<=16;j++)
{
for(int i=1;i<=n;i++)
{
up[i][j]=up[up[i][j-1]][j-1];
}
}
}
int lca(int lx,int rx)
{
if(dep[lx]<dep[rx])
swap(lx,rx);
int a=rx,b=lx;
int c=dep[b]-dep[a];
for(int i=0;i<=16;i++)
{
if(c&(1<<i))
b=up[b][i];
}
if(a==b)
return a;
for(int i=16;i>=0;i--)
{
if(up[a][i]!=up[b][i])
{
a=up[a][i];
b=up[b][i];
}
}
return fa[a];
}
int ask(int lx,int rx,int k)
{
int l=1,r=tsize;
int mx=lca(lx,rx);
int f=fa[mx];
lx=t[lx];
rx=t[rx];
mx=t[mx];
f=t[f];
while(l<r)
{
int m=(l+r)>>1;
// cout<<b[m]<<" "<<a[a[rx].l].tsize<<" "<<a[a[f].l].tsize<<" "<<a[a[lx].l].tsize<<" "<<a[a[mx].l].tsize<<endl;
// cout<<b[m]<<" "<<a[a[rx].r].tsize<<" "<<a[a[f].r].tsize<<" "<<a[a[lx].r].tsize<<" "<<a[a[mx].r].tsize<<endl;
if(a[a[rx].l].tsize-a[a[f].l].tsize+a[a[lx].l].tsize-a[a[mx].l].tsize>=k)
{
r=m;
lx=a[lx].l;
f=a[f].l;
mx=a[mx].l;
rx=a[rx].l;
}
else
{
l=m+1;
k-=a[a[rx].l].tsize-a[a[f].l].tsize+a[a[lx].l].tsize-a[a[mx].l].tsize;
lx=a[lx].r;
f=a[f].r;
mx=a[mx].r;
rx=a[rx].r;
}
}
return l;
}
void dfs(int x)
{
for(int i=0;i<w[x].size();i++)
{
if(book[w[x][i]])
continue;
book[w[x][i]]=1;
dep[w[x][i]]=dep[x]+1;
fa[w[x][i]]=x;
t[w[x][i]]=update(t[x],find_index(v[w[x][i]]));
dfs(w[x][i]);
}
return ;
}
int main()
{
int i,j,l,r,k;
cin>>n>>m;
for(i=1;i<=n;i++)
scanf("%d",&v[i]),b[i]=v[i];
for(i=1;i<n;i++)
{
scanf("%d %d",&l,&r);
w[l].push_back(r);
w[r].push_back(l);
}
hash1();
t[0]=build(1,tsize);
int root=1;
w[0].push_back(root);
book[0]=1;dep[0]=0;
dfs(0);
init();
while(m--)
{
cin>>l>>r>>k;
printf("%d\n",b[ask(l,r,k)]);
}
return 0;
}
求區間第k大值,不過有修改操作,需要套一個樹狀陣列,因為要對所有的點離散化,所以只能離線操作
自己試著寫了一個,在初始時就直接樹狀陣列操作,在對應點上建樹,這樣空間複雜度會高一寫,在zoj上也是無限段錯誤。。。無論陣列開多大。。。
按照kuangbin的模板修改了一下,先按照原來方式建樹,在更新時不修改原樹,而是建一個新樹存修改的值,,這樣好像空間省點。。。。然而還是段錯誤,仿照程式碼改了很長時間,直到把陣列大小剛好開在了2500010時過了。。。改成3000010會提示記憶體超限,再大一些就是段錯誤。。。。。迷之段錯誤。。。
#include<bits/stdc++.h>
using namespace std;
const int maxn=60010;
const int M=2000010;
int sz,n,lagen,m,tot=0,v[maxn],b[maxn],s[maxn],t[maxn],use[maxn];
struct node
{
int l,r,sz;
}a[M];
struct question
{
char c;
int i,j,k;
};
int build(int l,int r)
{
int root=tot++;
a[root].sz=0;
if(l==r)
return root;
int m=(l+r)>>1;
a[root].l=build(l,m);
a[root].r=build(m+1,r);
return root;
}
int update(int root,int x,int k)
{
int now=tot++;
int temp=now;
int l=0,r=sz-1,m;
a[now].sz=a[root].sz+k;
while(l<r)
{
m=(l+r)>>1;
if(x<=m)
{
a[now].l=tot++;
a[now].r=a[root].r;
now=a[now].l;
root=a[root].l;
r=m;
}
else
{
a[now].r=tot++;
a[now].l=a[root].l;
now=a[now].r;
root=a[root].r;
l=m+1;
}
a[now].sz=a[root].sz+k;
}
return temp;
}
int add(int index,int x,int k)
{
while(index<=n)
{
s[index]=update(s[index],x,k);
index+=index&-index;
}
}
int ask(int lx,int rx,int k)
{
int i,j;
int lx_root=t[lx];
int rx_root=t[rx];
for(i=lx;i;i-=i&-i)
use[i]=s[i];
for(i=rx;i;i-=i&-i)
use[i]=s[i];
int l=0,r=sz-1,m;
while(l<r)
{
m=(l+r)>>1;
int sum=0;
for(i=lx;i;i-=i&-i)
sum-=a[a[use[i]].l].sz;
for(i=rx;i;i-=i&-i)
sum+=a[a[use[i]].l].sz;
sum+=a[a[rx_root].l].sz-a[a[lx_root].l].sz;
if(sum>=k)
{
r=m;
for(i=lx;i;i-=i&-i)
use[i]=a[use[i]].l;
for(i=rx;i;i-=i&-i)
use[i]=a[use[i]].l;
lx_root=a[lx_root].l;
rx_root=a[rx_root].l;
}
else
{
l=m+1;
k-=sum;
for(i=lx;i;i-=i&-i)
use[i]=a[use[i]].r;
for(i=rx;i;i-=i&-i)
use[i]=a[use[i]].r;
lx_root=a[lx_root].r;
rx_root=a[rx_root].r;
}
}
return l;
}
void hash1()
{
sort(b,b+lagen);
sz=unique(b,b+lagen)-b;
}
int find_index(int x)
{
return lower_bound(b,b+sz,x)-b;
}
vector<question>w;
int main()
{
int T,i;
cin>>T;
while(T--)
{
tot=lagen=0;
cin>>n>>m;
for(i=1;i<=n;i++)
scanf("%d",&v[i]),b[lagen++]=v[i];
question temp;
while(m--)
{
getchar();
temp.c=getchar();
if(temp.c=='Q')
{
scanf("%d %d %d",&temp.i,&temp.j,&temp.k);
}
else
{
scanf("%d %d",&temp.i,&temp.j);
b[lagen++]=temp.j;
}
w.push_back(temp);
}
hash1();
t[0]=build(1,sz);
for(i=1;i<=n;i++)
{
t[i]=update(t[i-1],find_index(v[i]),1);
s[i]=t[0];
}
for(i=0;i<w.size();i++)
{
if(w[i].c=='Q')
{
printf("%d\n",b[ask(w[i].i-1,w[i].j,w[i].k)]);
}
else
{
add(w[i].i,find_index(v[w[i].i]),-1);
add(w[i].i,find_index(w[i].j),1);
v[w[i].i]=w[i].j;
}
}
w.clear();
}
}
查詢區間不同值的個數
樹狀陣列可以離線搞,先把所有查詢存下來,然後按照右端點的值排序,樹狀陣列存的值為0或1,表示這個下標有沒有值,開一個標記陣列,保證處理過程中原陣列每個元素只出現一次
#include<bits/stdc++.h>
#define eps 1e-9
#define PI 3.141592653589793
#define bs 1000000007
#define bsize 256
#define MEM(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn=30005,maxm=200005;
struct question
{
int l,r,index;
bool operator<(const question a)const
{
if(r==a.r)
return l<a.l;
return r<a.r;
}
}query[maxm];
int n,f[maxn],a[maxn],ans[maxm],book[1000005];
int add(int x,int k)
{
while(x<=n)
{
f[x]+=k;
x+=x&-x;
}
}
int sum(int x)
{
int ans=0;
while(x)
{
ans+=f[x];
x-=x&-x;
}
return ans;
}
int main()
{
cin>>n;
int i,q;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
cin>>q;
for(i=0;i<q;i++)
{
scanf("%d %d",&query[i].l,&query[i].r);
query[i].index=i;
}
sort(query,query+q);
int now=0;
for(i=1;i<=n&&now<q;i++)
{
if(book[a[i]])
add(book[a[i]],-1);
add(i,1);
book[a[i]]=i;
while(query[now].r<=i&&now<q)
{
ans[query[now].index]=sum(query[now].r)-sum(query[now].l-1);
now++;
}
}
for(i=0;i<q;i++)
printf("%d\n",ans[i]);
return 0;
}
主席樹可以實現線上搞,每個節點儲存的線段樹存的資訊為每個點的值,也是1或0,使用標記陣列記錄此數之前出現的位置(下標),我當前節點的線段樹,和前一個節點有許多公共使用的部分,這樣更新數值所在的新的位置,也就是現在所在的下標,在把標記數組裡記錄的這個值上一個位置給賦零,這樣當前節點的線段樹就記錄了當前位置和左邊任意位置之間不同數的個數、
#include<bits/stdc++.h>
#define eps 1e-9
#define PI 3.141592653589793
#define bs 1000000007
#define bsize 256
#define MEM(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn=30005,maxm=200005;
struct node
{
int l,r,sz;
}a[100*maxn];
int book[1000005],tot=0,t[maxn],n,v[maxn];
int build(int l,int r)
{
int root=tot++;
a[root].sz=0;
int m=(l+r)>>1;
if(l!=r)
{
a[root].l=build(l,m);
a[root].r=build(m+1,r);
}
return root;
}
int update(int root,int x,int k)
{
int l=1,r=n;
int now=tot++;
int m,temp=now;
a[now].sz=a[root].sz+k;
while(l<r)
{
m=(l+r)>>1;
if(x<=m)
{
a[now].r=a[root].r;
a[now].l=tot++;
now=a[now].l;
root=a[root].l;
r=m;
}
else
{
a[now].l=a[root].l;
a[now].r=tot++;
now=a[now].r;
root=a[root].r;
l=m+1;
}
a[now].sz=a[root].sz+k;
}
return temp;
}
int ask(int root,int x)
{
int l=1,r=n,m;
int ans=a[root].sz;
while(l<r)
{
m=(l+r)>>1;
if(x<=m)
{
root=a[root].l;
r=m;
}
else
{
ans-=a[a[root].l].sz;
root=a[root].r;
l=m+1;
}
}
return ans;
}
int main()
{
cin>>n;
int i,q;
t[0]=build(1,n);
for(i=1;i<=n;i++)
{
scanf("%d",&v[i]);
if(book[v[i]])
{
t[i]=update(t[i-1],book[v[i]],-1);
t[i]=update(t[i],i,1);
}
else
t[i]=update(t[i-1],i,1);
book[v[i]]=i;
}
cin>>q;
int l,r;
for(i=0;i<q;i++)
{
scanf("%d %d",&l,&r);
printf("%d\n",ask(t[r],l));
}
return 0;
}
還有一個類似的例題,查詢區間不同數的個數,不過加了一些操作強制了線上查詢,不能使用樹狀陣列
#include<bits/stdc++.h>
#define eps 1e-9
#define PI 3.141592653589793
#define bs 1000000007
#define bsize 256
#define MEM(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn=200005;
struct node
{
int l,r,sz;
}a[50*maxn];
int book[maxn],tot=0,t[maxn],n,v[maxn],ans[maxn];
int build(int l,int r)
{
int root=tot++;
a[root].sz=0;
int m=(l+r)>>1;
if(l!=r)
{
a[root].l=build(l,m);
a[root].r=build(m+1,r);
}
return root;
}
int update(int root,int x,int k)
{
int l=1,r=n;
int now=tot++;
int m,temp=now;
a[now].sz=a[root].sz+k;
while(l<r)
{
m=(l+r)>>1;
if(x<=m)
{
a[now].r=a[root].r;
a[now].l=tot++;
now=a[now].l;
root=a[root].l;
r=m;
}
else
{
a[now].l=a[root].l;
a[now].r=tot++;
now=a[now].r;
root=a[root].r;
l=m+1;
}
a[now].sz=a[root].sz+k;
}
return temp;
}
int ask(int root,int x)
{
int l=1,r=n,m;
int ans=0;
while(l<r)
{
m=(l+r)>>1;
if(x<m)
{
root=a[root].l;
r=m;
}
else
{
ans+=a[a[root].l].sz;
root=a[root].r;
l=m+1;
}
}
return ans;
}
int solve(int root,int x)
{
int temp=ask(root,x);
int k=(temp+1)/2;
int l=1,r=n,m;
while(l<r)
{
m=(l+r)>>1;
if(a[a[root].l].sz>=k)
{
root=a[root].l;
r=m;
}
else
{
k-=a[a[root].l].sz;
root=a[root].r;
l=m+1;
}
}
return l;
}
int main()
{
int T,i,q;
cin>>T;
for(int tcase=1;tcase<=T;tcase++)
{
tot=0;
memset(book,0,sizeof(book));
cin>>n>>q;
t[n+1]=build(1,n);
for(i=1;i<=n;i++)
scanf("%d",&v[i]);
for(i=n;i>=1;i--)
{
if(book[v[i]])
{
t[i]=update(t[i+1],book[v[i]],-1);
t[i]=update(t[i],i,1);
}
else
t[i]=update(t[i+1],i,1);
book[v[i]]=i;
}
int l,r,le,ri;
ans[0]=0;
for(i=1;i<=q;i++)
{
scanf("%d %d",&le,&ri);
l=min((ans[i-1]+le)%n+1,(ans[i-1]+ri)%n+1);
r=max((ans[i-1]+le)%n+1,(ans[i-1]+ri)%n+1);
ans[i]=solve(t[l],r);
}
printf("Case #%d:",tcase);
for(i=1;i<=q;i++)
{
printf(" %d",ans[i]);
}
cout<<endl;
}
return 0;
}
相關推薦
主席樹模板總結及題集
介紹篇:http://blog.csdn.net/metalseed/article/details/8045038 -> 理解篇(配圖比較生動):http://blog.csdn.net/regina8023/article/details/41910615 -
POJ2104主席樹模板題
root contains mem void con gin bsp 情況 scan 完成新成就——B站上看了算法https://www.bilibili.com/video/av4619406/?from=search&seid=17909
【POJ 2104】【主席樹模板題】K-th Number
題意: 靜態詢問區間第K大問題。給出一個數組,然後多次詢問某一區間第K大數是多少。 思路: 典型的主席樹模板題。 所以就大致講一下靜態主席樹
POJ 2104 K-th Number 主席樹模板題
題意:給定一個長度為n的無序陣列,然後有m組詢問l r k,求區間[l,r]中的第k大數 思路:以前用劃分樹做過,現在學習主席樹,模板題。個人對主席樹的理解:就是把線段樹更新過程中所有的歷史狀態記錄下來,例如更新m次的話,那麼就會有m種狀態,直接建m棵線段樹的話,時間和空
Link-Cut Tree(LCT) 模板總結 & 水題/模板題 動態樹
虛擬碼解釋LCT模板,下面題目有C++模板。 splay::push_up i: // 維護資料 // 舉個例子 i.sum = i.left_child.sum + i.right_child.sum splay::push_down i: // 下傳翻轉
4417_Super Mario_主席樹模板題
題意 區間【1, n】上,每個點都有一塊處在某高度的石頭。給 m 個詢問,區間【l, r】上高度不大於 h 的石頭有多少塊。 思路 主席樹模板題 連結 程式碼 #include<cstdio> #include<iostre
主席樹模板
else har body 內存 fin AC div 區間第k大 cstring 這裏沒有思想,沒有光,沒有熱,只有寒冷和永無止境的黑暗。 還有.赤裸裸的主席樹代碼(求區間第K大) 註意保險起見內存開30倍 1 #include<iostream> 2
樹-概念性總結及代碼示例
取出 次數 術語 結構 無法 浪費 滿二叉樹 否則 實現 總覽: 樹的基本概念 二叉樹 樹和森林 樹與二叉樹的應用 樹的基本概念 樹的定義:若幹結點的集合,有唯一根結點,無環,結點個數可為0 樹的基本術語: 結點的度:結點擁有的子樹個數或者分支的個數 樹的度:結點的度的
HDU 2665.Kth number-無修改區間第K小-可持久化線段樹(主席樹)模板
sort ota nbsp ani show 去重 第k小 math urn Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth
主席樹模板(轉)~
#include <iostream> #include <cstdio> using namespace std; const int MAX = 20000010; struct Persistable_Segment_tree { int ls,rs,v;//分別是該
主席樹模板(動態)
#include<bits/stdc++.h> using namespace std; const int N=2e6+10; int n,m,sz,tot,a[N],b[N],rt[N],s[N],ls[N*30],rs[N*30],sum[N*30],rootl[40],root
主席樹模板(支援修改)
#include<bits/stdc++.h> using namespace std; const int N=2e6+10; int n,m,sz,tot,a[N],b[N],rt[N],s[N],ls[N*30],rs[N*30],sum[N*30],rootl[40],rootr
AC的故事大結局悲劇版(下) 主席樹模板,區間修改
現有m個操作,分三類: A:l 到r 營地在新的一天裡新增士兵k名(隨後時間從t 現在變成t+1了) B:問第i天 l 到r 營地共有士兵幾名。 C:時間倒流到x天 。(厲害了。。。) /// .-
SPOJ DQUERY (主席樹模板)
題意: 給出一個序列,詢問區間內有多少個不同的數 這題卡分塊莫隊,寫了一下主席樹,已加入模板 主席樹大概是這麼回事,每個結點記錄字首線段樹,當然這裡的線段樹結點的申請是動態的,每次最多申請logn個,對於詢問來說就只需要詢問字首r線段樹中l到n區間內不同數的個數了 #i
poj 2104 K-th Number (主席樹模板)
傳送門 // by spli #include<cstring> #include<cstdio> #include<algorithm> #include<iostream> using namespace
不帶修改主席樹模板
對於一部分線段樹看似無法直接做的題,可以用主席樹來做。 主席樹就是對每個字首開一棵線段樹,當然,直接這樣會MLE。 可以使用一種類似動態開節點的方法可以有效避免MLE。 具體可以參考我的部落格,那
關於樹刨的一些題集
getch tar head 記得 cst esp ins etc space [ZJOI2008]樹的統計 很裸的一道樹刨題,主要是線段樹維護一下最大值即可 直接上代碼了,這道題我沒怎麽刻意去卡常數,最慢的一個998ms很神奇吧 #include<bits
【刷題】洛谷 P3834 【模板】可持久化線段樹 1(主席樹)
!= tchar 這樣的 信息 reg har mem hair define 題目背景 這是個非常經典的主席樹入門題——靜態區間第K小 數據已經過加強,請使用主席樹。同時請註意常數優化 題目描述 如題,給定N個正整數構成的序列,將對於指定的閉區間查詢其區間內的第K小值。
poj 2104 K-th Number (主席樹入門模板題)
摘抄了一段主席樹的解釋:所謂主席樹呢,就是對原來的數列[1..n]的每一個字首[1..i](1≤i≤n)建立一棵線段樹,線段樹的每一個節點存某個字首[1..i]中屬於區間[L..R]的數一共有多少個(比如根節點是[1..n],一共i個數,sum[root]
poj2104 K-th Number (主席樹入門題|模板題)
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 48751 Accepted: 16447 Case Time Limit: 2000MS Description Y