线段树 BZOJ NOI 十连测
【问题描述】
小𝐷写完了随机哈夫曼树,凭信仰开始补觉,想起了在ZJOI见过的一
个模型:
∙ 给出一个长度为𝑛的数组𝑎,1 ∼ 𝑛标号。
∙ 𝑚个操作,每个操作有两个参数𝑙𝑖, 𝑟𝑖,表示将区间[𝑙𝑖, 𝑟𝑖]中的所有数修
改为这个区间的最大值。
这个问题可以简单地用线段树来完成,现在小𝐷将这个问题扩展了一
下。
给出一个长度为𝑛的初始数组𝑎,以及𝑚个操作,需要维护:
∙ 求依次进行编号为[𝐿,𝑅]的这一段操作后𝑎𝑘的值。这些询问互相独立,
即可以理解为“假如进行这一段操作后,𝑎𝑘的值会变成多少”。
∙ 将𝑎𝑢的值修改为𝑣,即修改初始数组,这次修改会影响它之后的所有
询问。
可以参考输入格式来帮助理解题意。
【输入格式】
从文件segment.in中读入数据。
第一行,三个正整数𝑛, 𝑚, 𝑞,表示数组长度,操作数和询问数。
第二行,𝑛个正整数𝑎𝑖。
【输出格式】
输出到文件segment.out中。
对于每次询问输出一行一个正整数,表示“假如进行编号为[𝐿,𝑅]的这
一段操作后𝑎𝑘的值”。
【数据规模】
对于10%的数据,𝑛, 𝑚, 𝑞 ≤ 100。
对于30%的数据,𝑛, 𝑚, 𝑞 ≤ 10000。
对于另20%的数据,询问时𝐿 = 1。
对于80%的数据,𝑛, 𝑚, 𝑞 ≤ 50000(包含前一档𝐿 = 1)。
对于100%的数据,𝑛, 𝑚, 𝑞 ≤ 105,1 ≤ 𝑎𝑖, 𝑣 ≤ 105。
SOL:
我们倒着做,发现最后的答案是原序列的一段区间的max,我们倒着加操作可以保证这个L与R单调。我们把L与R差点,
我们考虑用主席树分别维护L与R,强行套个倍增就好了。
#include<bits/stdc++.h> using namespace std; #define N 300007 #define Mid (l+r>>1) #define Max(a,b) ((a)>(b)?(a):(b)) #define sight(x) ('0'<=x&&x<='9') int ans,anw,n,m,q,root[N],L[N][21],R[N][21],op,l,r,w,LL,RR; inline void read(int &x){ static char c; for (c=getchar();!sight(c);c=getchar()); for (x=0;sight(c);c=getchar())x=x*10+c-48; } struct Node{ int l,r; }qu[N]; void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);} inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); } inline void writel(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); } struct Tree{ int t[N<<2]; void build(int node,int l,int r){ if (l==r) {read(t[node]); return;} build(node<<1,l,Mid); build(node<<1|1,Mid+1,r); t[node]=Max(t[node<<1],t[node<<1|1]); } void change(int node,int l,int r,int id,int ch){ if (l==r) {t[node]=ch; return;} if (id<=Mid) change(node<<1,l,Mid,id,ch); else change(node<<1|1,Mid+1,r,id,ch); t[node]=Max(t[node<<1],t[node<<1|1]); } void Qu(int node,int l,int r,int L,int R){ if (L<=l&&r<=R) {ans=Max(ans,t[node]); return;} if (L<=Mid) Qu(node<<1,l,Mid,L,R); if (Mid<R) Qu(node<<1|1,Mid+1,r,L,R); } }T1; struct Te{ int l,r,col; }; struct TimeTree{ Te T[N<<4];int tot; void add(int p,int &now,int l,int r,int L,int R,int dla){ now=++tot; T[now]=T[p]; if (L<=l&&r<=R) {T[now].col=dla; return;} if (L<=Mid) add(T[p].l,T[now].l,l,Mid,L,R,dla); if (Mid<R) add(T[p].r,T[now].r,Mid+1,r,L,R,dla); } void Qu(int X,int l,int r,int x){ ans=Max(ans,T[X].col); if (l==r) return; if (x<=Mid) Qu(T[X].l,l,Mid,x); else Qu(T[X].r,Mid+1,r,x); } }T2; struct UnTree{ int col[N<<2]; void add(int node,int l,int r,int L,int R,int dla){ if (L<=l&&r<=R){ col[node]=dla;return;} if (L<=Mid) add(node<<1,l,Mid,L,R,dla); if (R>Mid) add(node<<1|1,Mid+1,r,L,R,dla); } void Qu(int X,int l,int r,int x){ ans=Max(ans,col[X]); if (l==r) return; if (x<=Mid) Qu(X<<1,l,Mid,x); else Qu(X<<1|1,Mid+1,r,x); } }T3; signed main () { freopen("segment.in","r",stdin); freopen("segment.out","w",stdout); read(n); read(m); read(q); T1.build(1,1,n); for (int i=1;i<=m;i++) read(qu[i].l), read(qu[i].r); for (int i=1;i<=m;i++) T2.add(root[i-1],root[i],1,n,qu[i].l,qu[i].r,i); for (int i=1;i<=m;i++) { ans=0; T3.Qu(1,1,n,qu[i].l); L[i][0]=ans; ans=0; T3.Qu(1,1,n,qu[i].r); R[i][0]=ans; T3.add(1,1,n,qu[i].l,qu[i].r,i); } for (int i=1;i<=19;i++) for (int j=1;j<=m;j++) { L[j][i]=L[L[j][i-1]][i-1]; R[j][i]=R[R[j][i-1]][i-1]; } while (q--) { read(op); read(l); read(r); if (op&1) T1.change(1,1,n,l,r); else { read(w); ans=0; T2.Qu(root[r],1,n,w); anw=ans; if (ans>=l) { for (int i=19;~i;i--) if (L[ans][i]>=l) ans=L[ans][i]; for (int i=19;~i;i--) if (R[anw][i]>=l) anw=R[anw][i]; LL=qu[ans].l; RR=qu[anw].r; ans=0; T1.Qu(1,1,n,LL,RR); }else ans=0,T1.Qu(1,1,n,w,w); writeln(ans); } } return 0; }