樹套樹之線段樹套線段樹(BZOJ3110、洛谷P3332)
阿新 • • 發佈:2018-12-24
前置技能
當然是線段樹啦!
應用及實現
線段樹可以維護一個序列。當需要維護一個矩陣(即二維平面)內的數值,或者有什麼奇怪的區間操作時,就需要用到二維線段樹,也就是線段樹“套上”線段樹。
當維護一個矩陣時,先建一顆“外面的”線段樹來維護一維,對於每個“外面的”線段樹的節點,建一顆“裡面的”線段樹來維護第二維。因為博主太懶沒有寫過這裡就直接貼大佬的Blog好了。
模板
當維護區間奇怪操作時,一般的套路是外面套權值線段樹,裡面套區間線段樹。比如說BZOJ3110(洛谷P3332)這道題,它要求我們維護n個位置,修改操作 l,r]
因為插入的權值≤maxlongint,總操作≤50000次,因此需要離散。
但是因為資料太水所以不離散也能過儘管BZOJ說加強了資料我也不知道怎麼回事
對於插入操作,單點修改權值線段樹,區間修改區間線段樹(區間線段樹維護的是當前區間這個數出現的次數)。
對於查詢操作,類似二分的在權值線段樹上遞迴,每次統計這個權值線段樹右子樹對應的區間線段樹在詢問區間內的區間和。如果答案(ans)≥當前排名就遞迴右子樹的第k大,否則遞迴左子樹的第k-ans大。
程式碼(好像離散比不離散在BZOJ上快了近一秒鐘):
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define N 50005
using namespace std;
typedef long long LL;
struct intree{ int ls,rs; LL s,lz; }t[N<<8];
struct query{ int f,l,r; LL w; }q[N];
int n,m,nd,num,rt[N<<8];
LL a[N];
inline char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF; return *l++;
}
inline LL _read(){
int x=0,f=1; char ch=readc();
while (!isdigit(ch)) { if (ch=='-') f=-1; ch=readc(); }
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x*f;
}
inline void writec(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) writec(x/10); putchar(x%10+'0');
}
inline void _write(int x){ writec(x),putchar('\n'); }
inline void nsrt(int &x,int l,int r,int p,int q){
if (!x) x=++nd; t[x].s+=(LL)q-p+1;
if (l>=p&&r<=q) return (void)++t[x].lz;
int mid=l+r>>1;
if (q<=mid) return nsrt(t[x].ls,l,mid,p,q);
if (p>mid) return nsrt(t[x].rs,mid+1,r,p,q);
nsrt(t[x].ls,l,mid,p,mid),nsrt(t[x].rs,mid+1,r,mid+1,q);
}
inline void build(int x,int l,int r,int p,int q,int w){
nsrt(rt[x],1,n,p,q);
if (l==r) return; int mid=l+r>>1;
if (w<=mid) build(x<<1,l,mid,p,q,w);
else build(x<<1|1,mid+1,r,p,q,w);
}
inline LL srch(int x,int l,int r,int p,int q){
if (!x) return 0; int mid=l+r>>1;
if (l>=p&&r<=q) return t[x].s;
LL ans=t[x].lz*(LL)(q-p+1);
if (q<=mid) return ans+srch(t[x].ls,l,mid,p,q);
if (p>mid) return ans+srch(t[x].rs,mid+1,r,p,q);
return ans+srch(t[x].ls,l,mid,p,mid)+srch(t[x].rs,mid+1,r,mid+1,q);
}
inline int ef(int x,int l,int r,int p,int q,LL w){
if (l==r) return l; int mid=l+r>>1;
LL pd=srch(rt[x<<1|1],1,n,p,q);
if (pd<w) return ef(x<<1,l,mid,p,q,w-pd);
return ef(x<<1|1,mid+1,r,p,q,w);
}
inline int calc(LL x){
return lower_bound(a+1,a+num+1,x)-a-1;
}
int main(){
n=_read(),m=_read();
for (int i=1;i<=m;i++){
q[i].f=_read(),q[i].l=_read(),q[i].r=_read(),q[i].w=_read();
if (q[i].f==1) a[++num]=q[i].w;
}
sort(a+1,a+num+1),num=unique(a+1,a+num+1)-a-1;
for (int i=1;i<=m;i++)
if (q[i].f==1)
build(1,1,num,q[i].l,q[i].r,calc(q[i].w)+1);
else _write(a[ef(1,1,num,q[i].l,q[i].r,q[i].w)]);
return 0;
}