主席樹剛接觸覺得超強,根本看不懂,看了幾位dalao的**後終於理解了主席樹。
先看一道例題:傳送門
題目大意:
假設我們預處理出了每個數滿足條件的最右邊界。
先考慮暴力做法,直接對x~y區間暴枚,求出答案。
主席樹做法:設主席樹的下標表示邊界為i的資訊。
主席樹是一棵可持久化線段樹,可以找出線段樹的歷史版本。
主席樹的空間複雜度可以達到o((n+m)logn
)(無修改的情況下)。
因為每一次修改至多修改logn個點。
有圖為證:
對於這道題,主席樹維護的是邊界為i的總和和總共有幾個這樣的點。
注意這裡主席樹的下標(i)是這個版本邊界這個數為i。
主席樹建樹其實是對字首1~i的建樹。
因為你之前1~i-1建過樹,你建的1~i版本是在1~i-1版本的基礎上建立的。
邊界就是你之前預處理出來的邊界。
更新操作:
void updata(int l,int r,int &x,int y,long查詢和:long
v)
int mid=l+r>>1
;
if(mid>=v)updata(l,mid,t[x].l,t[y].l,v);//訪問左節點
else updata(mid+1
,r,t[x].r,t[y].r,v);//訪問右節點
t[x].sum=t[t[x].l].sum+t[t[x].r].sum;
t[x].tot=t[t[x].l].tot+t[t[x].r].tot;
}
long查詢區間內有多少數:long qsum(int l,int r,int x,int y,int ql,int
qr)//在查詢區間內
int mid=l+r>>1
;
long
long ans=0
;
if(mid>=ql)ans+=qsum(l,mid,t[x].l,t[y].l,ql,qr);
if(mid1
,r,t[x].r,t[y].r,ql,qr);
return
ans;
}
longall code:(被注釋的部分是暴力**)long qcnt(int l,int r,int x,int y,int ql,int
qr)//在查詢區間內
int mid=l+r>>1
;
long
long ans=0
;
if(mid>=ql)ans+=qcnt(l,mid,t[x].l,t[y].l,ql,qr);
if(mid1
,r,t[x].r,t[y].r,ql,qr);
return
ans;
}
#include #includeusing
namespace
std;
char
tc()
intread()
const
int maxn=4*1e5;
long
long n,q,seed,a[maxn+5],sum[maxn+5],p[31][2
];long
long nxt[maxn+5
];struct
nodet[maxn*40
];int root[maxn+5
],cnt;
void updata(int l,int r,int &x,int y,long
long
v)
int mid=l+r>>1
;
if(mid>=v)updata(l,mid,t[x].l,t[y].l,v);
else updata(mid+1
,r,t[x].r,t[y].r,v);
t[x].sum=t[t[x].l].sum+t[t[x].r].sum;
t[x].tot=t[t[x].l].tot+t[t[x].r].tot;
}long
long qsum(int l,int r,int x,int y,int ql,int
qr)
int mid=l+r>>1
;
long
long ans=0
;
if(mid>=ql)ans+=qsum(l,mid,t[x].l,t[y].l,ql,qr);
if(mid1
,r,t[x].r,t[y].r,ql,qr);
return
ans;
}long
long qcnt(int l,int r,int x,int y,int ql,int
qr)
int mid=l+r>>1
;
long
long ans=0
;
if(mid>=ql)ans+=qcnt(l,mid,t[x].l,t[y].l,ql,qr);
if(mid1
,r,t[x].r,t[y].r,ql,qr);
return
ans;
}int buf[90
];void printf(long
long
x)int
main()
}/*for(int i=1;i<=n;i++)
}if(!nxt[i])nxt[i]=n;
}*/for(i=1;i<=n;i++)
updata(
1,n,root[i],root[i-1
],nxt[i]);
q=read();
long
long ans=0
,qs,qc,x,y;
for(i=q;i;i--)
return0;
}
主席樹 初學
現在才開始學主席樹 弱 不過不帶修改的話 還是很簡單的嘛。或者說應該叫可持久化線段樹?首先對數的區間進行離散化,這樣下面的a i 都預設為離散化以後的結果了。對於每個1.i開乙個線段樹,對於這個線段樹中的每乙個節點 l,r 表示1.i中在 l,r 中的數的個數。顯然這n個線段樹的形態大小是完全一樣的...
主席樹 模板
思想 主席樹就是一顆持久化線段樹,為什麼叫持久化了,因為它可以儲存之前的線段樹版本,並且可以拿來用,從而優化空間.至於為什麼叫主席樹了,大概是因為發明這個演算法的人的名字的緣故吧 詳細說說 主席樹是一種離線資料結構,是由很多棵線段樹組成的。第i棵線段樹存的是前i個數的資訊 每乙個線段存數字的出現次數...
主席樹模板
維護n棵1 i的字首權值線段樹,每次查詢減一下就好了。poj 2104就是模板題,裸的靜態第k大,需要先離散化,不會的就用lower bound 多試試,研究研究應該就能懂。include include include include using namespace std const int m...