简单莫队

莫队

正常莫队时间(左端点+右端点)复杂度:

\[O(mS+\frac{n^2}{S}) \]

\(S=\frac{n}{\sqrt m}\)时取到最小

带修莫队时间复杂度:

\[O(nB+\frac{n^2}{S}+\frac{n^3}{S^2}) \]

大约在\(S=n^{\frac{2}{3}}\)时取到最小值

例题1

Luogu P3245 [HNOI2016]大数

考虑如何快速求出\([l,r]\)的商

不妨\(s[r]=\sum_{i=1}^{r}10^{r-i}\times a[i]\)

则显然\([l,r]\)的答案可以表示为\(s[r]-s[l-1]\times 10^{r-l+1}\)(比较好做,毕竟相同的十足求相同的就行,如果另外一种方式就需要两个不同的数组找相同的元素)

则:

\[s[r]-s[l-1]\times 10^{r-l+1}=0(modp) \]

可以变成:

\[s[r]\times 10^{-r}=s[l-1]\times 10^{-l+1}(modp) \]

显然需要逆元,而逆元存在的条件是\(gcd(10,p)=1\),所以需要特判p=2,p=5

然后统计\([l,r+1]\)中相同大小的数的对数,显然需要莫队

而特判只需要末尾是偶数或者0,5即可

本来可以直接预处理求出位置和个数的前缀和然后\(O(n)\)解决

但我傻逼了,也写了莫队

WA了一次,没画图

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=2e5+5;
int n,m,p,s[N],bl[N],S;
char a[N];
ll Ans[N];
struct A{
	int l,r,id;
}Q[N];
inline bool cmp(A i,A j) {
	return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&i.r<j.r;
}
namespace www {
	ll ans; int sum;
	inline void add(int i) {
		if(!((a[i]-'0')&1)) sum++,ans+=i;
	}
	inline void dec(int i) {
		if(!((a[i]-'0')&1)) sum--,ans-=i;
	}
	void main() {
		for(int i=1;i<=m;i++) {
			scanf("%d%d",&Q[i].l,&Q[i].r);
			Q[i].id=i; 
		}
		sort(Q+1,Q+m+1,cmp);
		int ql=1,qr=0;
		for(int i=1;i<=m;i++) {
			while(Q[i].l<ql) ql--,add(ql);
			while(Q[i].l>ql) dec(ql),ql++;
			while(Q[i].r>qr) qr++,add(qr);
			while(Q[i].r<qr) dec(qr),qr--;
			Ans[Q[i].id]=ans-(ll)sum*(Q[i].l-1);
		}
		for(int i=1;i<=m;i++) printf("%lld\n",Ans[i]);
	}
}
namespace sss {
	ll ans; int sum;
	inline void add(int i) {
		if((a[i]-'0')%5==0) sum++,ans+=i;
	}
	inline void dec(int i) {
		if((a[i]-'0')%5==0) sum--,ans-=i;
	}
	void main() {
		for(int i=1;i<=m;i++) {
			scanf("%d%d",&Q[i].l,&Q[i].r);
			Q[i].id=i; 
		}
		sort(Q+1,Q+m+1,cmp);
		int ql=1,qr=0;
		for(int i=1;i<=m;i++) {
			while(Q[i].l<ql) ql--,add(ql);
			while(Q[i].l>ql) dec(ql),ql++;
			while(Q[i].r>qr) qr++,add(qr);
			while(Q[i].r<qr) dec(qr),qr--;
			Ans[Q[i].id]=ans-(ll)sum*(Q[i].l-1);
		}
		for(int i=1;i<=m;i++) printf("%lld\n",Ans[i]);
	}
}
namespace fff {
	ll ans;
	int t[N],b[N];
	inline void add(int i) {
		ans+=t[s[i]];
		t[s[i]]++;
	}
	inline void dec(int i) {
		t[s[i]]--;
		ans-=t[s[i]];
	}
	inline bool cmp2(int i,int j) {
		return s[i]<s[j];
	}
	inline int ksm(ll a,int b) {
		ll ret=1;
		while(b){
			if(b&1) ret=ret*a%p;
			a=a*a%p,b>>=1;
		}
		return ret;
	}
	void main() {
		for(int i=1;i<=n;i++) {
			s[i]=((ll)s[i-1]*10+a[i]-'0')%p;
		}
		int t=1,fm=ksm(10,p-2);
		for(int i=1;i<=n;i++) {
			t=(ll)t*fm%p;
			s[i]=(ll)s[i]*t%p;
			b[i]=i;
		}
		sort(b,b+n+1,cmp2);
		t=1; int lst=s[b[0]]; s[b[0]]=t;
		for(int i=1;i<=n;i++) {
			if(s[b[i]]!=lst) ++t;
			lst=s[b[i]]; s[b[i]]=t;
		}
		for(int i=1;i<=m;i++) {
			scanf("%d%d",&Q[i].l,&Q[i].r);
			Q[i].l--;
			Q[i].id=i; 
		}
		sort(Q+1,Q+m+1,cmp);
		int ql=1,qr=0;
		for(int i=1;i<=m;i++) {
			while(Q[i].l<ql) ql--,add(ql);
			while(Q[i].l>ql) dec(ql),ql++;
			while(Q[i].r>qr) qr++,add(qr);
			while(Q[i].r<qr) dec(qr),qr--;
			Ans[Q[i].id]=ans;
		}
		for(int i=1;i<=m;i++) printf("%lld\n",Ans[i]);
	}
}
int main() {
	scanf("%d%s%d",&p,a+1,&m);
	n=strlen(a+1),S=sqrt(n);
	for(int i=1;i<=n;i++) bl[i]=(i-1)/S+1;
	if(p==2) www::main();
		else if(p==5) sss::main();
			else fff::main();
	return 0;
}

树上莫队

需要划分块,也就是把树分成了若干块,使得点之间的最大距离在一个较小的范围内

一般我选择DFS分块,就是做完以后将点加入队列,某个点的子树做完后队列中的元素个数超过S,将它们分到一个块中,据说块的最大大小是\([S,3S)\)

然后就是暴力地从\((u_1,v_1)\)转移到\((u_2,v_2)\),可以通过异或实现,异或表示是否被计算

可以暴力从\(u_1->u_2,v_1->v_2\)

在转移时不要算他们的LCA,因为往往LCA也在其中(红点需要取反)

但需要更正\((u_1,v_1)\)\((u_2,v_2)\) 的LCA,也就是对它们的LCA取反

可以发现\((u1,v1),(u2,v2)\)还需要更正1次

而且因为异或的性质不需要特判,每次都做即可

总结一下:

  • DFS划分块

  • 移动的时候不要计算LCA

  • 计算两组的LCA

模板

Luogu P4074 [WC2013] 糖果公园

和其他莫队一样,树上莫队也有自己的写法

Q[0].l=Q[0].r=rt; //未计算lca(rt),后面也就没必要再计算0的lca
while() {
	move...
    int l=lca(Q[i].l,Q[i].r)
    upd(lca)
  	give_ans...
    upd(lca)//计算两组的lca(先计算完了)
}

TLE了一次:分块直接在进入队列后就分了,应该是在做完子树后再分

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=1e5+5,M=1e6+5;
int n,m,q,S,bl[N],dep[N],f[N],t[M],st[N],top,a[N],b[N],c[N],col;
ll ans,Ans[N];
bool s[N]; 
vector<int>V[N];
struct A{ int x,y; }d[N];
struct B{int id,l,r,i; }Q[N];
void fk(int fa,int u) {
	dep[u]=dep[fa]+1,f[u]=fa;
	int lst=top;
	for(int v:V[u]) {
		if(v!=fa) {
			fk(u,v);
			if(top-lst>=S) {
				++col;
				for(int i=lst+1;i<=top;i++) {
					bl[st[i]]=col; 
				}
				top=lst+1;
			}
		}
	}
	st[++top]=u;
}
inline bool cmp(B i,B j) {
	return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&bl[i.r]<bl[j.r]||bl[i.l]==bl[j.l]&&bl[i.r]==bl[j.r]&&i.i<j.i;
}

inline void upd(int u) {
	s[u]^=1;
	if(s[u]==1) {
		t[c[u]]++;
		ans+=(ll)b[t[c[u]]]*a[c[u]];
	} else {
		ans-=(ll)b[t[c[u]]]*a[c[u]];
		t[c[u]]--;
	}
}
void Move(int u,int v) {
	while(u!=v) {
		if(dep[u]>dep[v]) swap(u,v);
		upd(v);
		v=f[v];
	}
}

namespace LCA {
	int dep[N],f[N][20],lg[N];
	int lca(int u,int v) {
		if(dep[u]>dep[v]) swap(u,v);
		while(dep[u]<dep[v]) v=f[v][lg[dep[v]-dep[u]]];
		if(u==v) return u;
		for(int i=lg[dep[u]];i>=0;i--) {
			if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; 
		}
		return f[u][0];
	}
	void dfs(int fa,int u) {
		dep[u]=!fa?0:dep[fa]+1,f[u][0]=fa;
		for(int i=1;i<=lg[dep[u]];i++) {
			f[u][i]=f[f[u][i-1]][i-1];
		}
		for(int v:V[u]) {
			if(v!=fa) {
				dfs(u,v);
			}
		}
	}
	inline void init() {
		lg[0]=-1;
		for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
		dfs(0,1);
	}
}
int main() {
	scanf("%d%d%d",&n,&m,&q); S=pow(n,0.667);
	for(int i=1;i<=m;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	for(int i=1;i<n;i++) {
		int u,v; scanf("%d%d",&u,&v);
		V[u].push_back(v),V[v].push_back(u);
	}
	for(int i=1;i<=n;i++) scanf("%d",&c[i]);
	LCA::init();
	fk(0,1);
	if(top) {
		++col;
		for(int i=1;i<=top;i++) bl[st[i]]=col;
		top=0;
	}
	m=0; int cnt=0;
	for(int i=1;i<=q;i++) {
		int op,x,y; scanf("%d%d%d",&op,&x,&y);
		if(!op) d[++cnt]=(A){x,y};
			else {
				m++; Q[m]=(B){m,x,y,cnt};
			}
	}
	sort(Q+1,Q+m+1,cmp);
	Q[0].l=Q[0].r=1; int qi=0;
	for(int i=1;i<=m;i++) {
		Move(Q[i-1].l,Q[i].l),Move(Q[i-1].r,Q[i].r);
		int l=LCA::lca(Q[i].l,Q[i].r); 
		upd(l);
		while(qi<Q[i].i){
			qi++; int u=d[qi].x;
			if(s[u]) {
				ans-=(ll)b[t[c[u]]]*a[c[u]]; 
				t[c[u]]--;
				swap(c[u],d[qi].y);
				t[c[u]]++;
				ans+=(ll)b[t[c[u]]]*a[c[u]];
			} else swap(c[u],d[qi].y);
		}
		while(qi>Q[i].i) {
			int u=d[qi].x;
			if(s[u]) {
				ans-=(ll)b[t[c[u]]]*a[c[u]]; 
				t[c[u]]--;
				swap(c[u],d[qi].y);
				t[c[u]]++;
				ans+=(ll)b[t[c[u]]]*a[c[u]];
			} else swap(c[u],d[qi].y);
			qi--;
		}
		Ans[Q[i].id]=ans;
		upd(l);
	}
	for(int i=1;i<=m;i++) {
		printf("%lld\n",Ans[i]);
	}
	return 0;
}

模板2

ybtoj F. 苹果树

TLE了一次:错在排序是第一关键字是bl,第二关键字不再是bl而用了它自己(数列习惯了)

因为树上它自己屁用也莫得,所以两个关键字都应该是bl(修改可以是自己)

#include<bits/stdc++.h>
using namespace std;

inline int rd() {
	int ret=0; char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	for(;isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-'0';
	return ret;
}
const int N=1e5+5;
int n,m,q,S,bl[N],dep[N],f[N],t[N],st[N],top,c[N],col,ans,Ans[N];
bool s[N]; 
vector<int>V[N];
struct A{ int id,l,r,a,b; }Q[N];

void fk(int fa,int u) {
	dep[u]=dep[fa]+1,f[u]=fa;
	int lst=top;
	for(int v:V[u]) {
		if(v!=fa) {
			fk(u,v);
			if(top-lst>=S) {
				++col;
				for(int i=lst+1;i<=top;i++) bl[st[i]]=col;
				top=lst;
			}
		}
	}
	st[++top]=u;
}
inline bool cmp(A i,A j) {
	return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&bl[i.r]<bl[j.r];
}
inline void upd(int u) {
	s[u]^=1;
	if(s[u]==1) {
		t[c[u]]++;
		if(t[c[u]]==1)ans++;
	} else {
		if(t[c[u]]==1) ans--;
		t[c[u]]--;
	}
}
void Move(int u,int v) {
	while(u!=v) {
		if(dep[u]>dep[v]) swap(u,v);
		upd(v);
		v=f[v];
	}
}

namespace LCA {
	int dep[N],f[N][20],lg[N];
	int lca(int u,int v) {
		if(dep[u]>dep[v]) swap(u,v);
		while(dep[u]<dep[v]) v=f[v][lg[dep[v]-dep[u]]];
		if(u==v) return u;
		for(int i=lg[dep[u]];i>=0;i--) {
			if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; 
		}
		return f[u][0];
	}
	void dfs(int fa,int u) {
		dep[u]=!fa?0:dep[fa]+1,f[u][0]=fa;
		for(int i=1;i<=lg[dep[u]];i++) {
			f[u][i]=f[f[u][i-1]][i-1];
		}
		for(int v:V[u]) {
			if(v!=fa) {
				dfs(u,v);
			}
		}
	}
	inline void init() {
		lg[0]=-1;
		for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
		dfs(0,1);
	}
}
int main() {
	n=rd(),m=rd(),S=sqrt(n);
	for(int i=1;i<=n;i++) c[i]=rd();
	for(int i=1;i<=n;i++) {
		int u=rd(),v=rd();
		V[u].push_back(v),V[v].push_back(u);
	}
	LCA::init();
	fk(0,1);
	if(top) {
		++col;
		for(int i=1;i<=top;i++) bl[st[i]]=col;
		top=0;
	}
	for(int i=1;i<=m;i++) {
		Q[i]=(A){i,rd(),rd(),rd(),rd()};
		if(bl[Q[i].l]>bl[Q[i].r]) swap(Q[i].l,Q[i].r);
	}
	sort(Q+1,Q+m+1,cmp);
	Q[0].l=Q[0].r=1;
	for(int i=1;i<=m;i++) {
		Move(Q[i-1].l,Q[i].l),Move(Q[i-1].r,Q[i].r);
		int l=LCA::lca(Q[i].l,Q[i].r); 
		upd(l);
		Ans[Q[i].id]=ans;
		if(Q[i].a!=Q[i].b&&t[Q[i].a]&&t[Q[i].b]) {
			Ans[Q[i].id]--;
		}
		upd(l);
	}
	for(int i=1;i<=m;i++) {
		printf("%d\n",Ans[i]);
	}
	return 0;
}

回滚莫队

用于解决不能加或者不能删的题目

自行百度

例题0

LuoguP5906 【模板】回滚莫队&不删除莫队
Wa了一次:在暴力做时忘记还原ret了

#include<bits/stdc++.h>
#define pb push_back
using namespace std;

const int N=2e5+5;
int n,S,m,fid[N],a[N],bl[N],t[N],tt[N],ans[N];
struct B{int *l; int r; };
vector<B>V;
struct A{int l,r,id; }q[N];
bool cmp(A i,A j) {
	return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&i.r<j.r;
}
int main(){
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	scanf("%d",&n); S=sqrt(n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]),fid[i]=a[i];
		bl[i]=(i-1)/S+1;
	}
	sort(fid+1,fid+n+1);
	int cnt=unique(fid+1,fid+n+1)-fid-1;
	for(int i=1;i<=n;i++) {
		a[i]=lower_bound(fid+1,fid+cnt+1,a[i])-fid;
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++) {
		scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	int fro=0,r=0,ret=0;
	for(int i=1;i<=m;i++) {
		if(i==1||bl[q[i].l]!=fro) {
			fro=bl[q[i].l];
			r=fro*S+1,ret=0;
			memset(t,0,sizeof(t));
			memset(tt,0,sizeof(tt));
		}
		if(fro==bl[q[i].r]) {
			for(int j=q[i].l;j<=q[i].r;j++) {
				if(!t[a[j]]) t[a[j]]=j;
					else ret=max(ret,j-t[a[j]]);
			}
			ans[q[i].id]=ret; ret=0;
			for(int j=q[i].l;j<=q[i].r;j++) {
				t[a[j]]=0;
			}
			continue;
		}
		for(;r<=q[i].r;r++) {
			tt[a[r]]=r;
			if(!t[a[r]]) t[a[r]]=r;
			ret=max(ret,tt[a[r]]-t[a[r]]);
		}
		V.clear(); V.pb((B){&ret,ret});
		for(int j=fro*S;j>=q[i].l;j--){
			V.pb((B){&t[a[j]],t[a[j]]});
			t[a[j]]=j;
			if(!tt[a[j]]) {
				V.pb((B){&tt[a[j]],tt[a[j]]});
				tt[a[j]]=j;
			}
			ret=max(ret,tt[a[j]]-t[a[j]]);
		}
		ans[q[i].id]=ret;
		for(int j=V.size()-1;j>=0;j--) {
			*V[j].l=V[j].r;
		}
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

例题1

ybtoj H. 小明日记

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=1e5+5;
int n,m,q,a[N],b[N],fid[N],S,bl[N],t[N];
ll ans,Ans[N],tmp;
struct A{int l,r,id; }Q[N];
inline bool cmp(int i,int j) {
	return a[i]<a[j];
}
inline bool cmp2(A i,A j) {
	return bl[i.l]<bl[j.l]||bl[i.l]==bl[j.l]&&i.r<j.r; 
}
int main() {
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=i;
	sort(b+1,b+n+1,cmp); 
	int m=1; fid[1]=a[b[1]],a[b[1]]=m;
	for(int i=2;i<=n;i++) {
		if(a[b[i]]!=fid[a[b[i-1]]]) {
			fid[++m]=a[b[i]];
		}
		a[b[i]]=m;
	}
	S=sqrt(n);
	for(int i=1;i<=n;i++) bl[i]=(i-1)/S+1;
	for(int i=1;i<=q;i++) {
		scanf("%d%d",&Q[i].l,&Q[i].r);
		Q[i].id=i;
	}
	sort(Q+1,Q+q+1,cmp2);
	for(int i=1;i<=q;) {
		memset(t,0,sizeof(t)),ans=0;
		for(;i<=q&&bl[Q[i].l]==bl[Q[i].r];i++) {
			for(int k=Q[i].l;k<=Q[i].r;k++) {
				t[a[k]]++;
				ans=max(ans,(ll)fid[a[k]]*t[a[k]]);
			}
			Ans[Q[i].id]=ans; ans=0;
			for(int k=Q[i].l;k<=Q[i].r;k++) {
				t[a[k]]--;
			}
		}
		for(int j=bl[Q[i].l]*S+1;j<=Q[i].r;j++) {
			t[a[j]]++;
			ans=max(ans,(ll)fid[a[j]]*t[a[j]]);
		}
		tmp=ans;
		for(int j=Q[i].l;j<=bl[Q[i].l]*S;j++) {
			t[a[j]]++;
			ans=max(ans,(ll)fid[a[j]]*t[a[j]]);
		}
		Ans[Q[i].id]=ans,ans=tmp;
		for(int j=Q[i].l;j<=bl[Q[i].l]*S;j++) {
			t[a[j]]--;
		}
		for(i++;i<=q&&bl[Q[i].l]==bl[Q[i-1].l];i++) {
			for(int j=Q[i-1].r+1;j<=Q[i].r;j++) {
				t[a[j]]++;
				ans=max(ans,(ll)fid[a[j]]*t[a[j]]);
			}
			tmp=ans;
			for(int j=Q[i].l;j<=bl[Q[i].l]*S;j++) {
				t[a[j]]++;
				ans=max(ans,(ll)fid[a[j]]*t[a[j]]);
			}
			Ans[Q[i].id]=ans,ans=tmp;
			for(int j=Q[i].l;j<=bl[Q[i].l]*S;j++) {
				t[a[j]]--;
			}
		}
	}
	for(int i=1;i<=q;i++) {
		printf("%lld\n",Ans[i]);
	}
	return 0;
}

例题2

ybtoj A. 值域连续
考虑莫队,需要删除,套线段树,记录连续最大,左边最大和右边最大,显然TLE
所以考虑回滚莫队,只需要加点和撤销
又因为这是一个排列,不重
所以只需要在每段连续数字的开头结尾维护答案,中间不需要处理
\(L[i]\)表示向左的最大长度,\(R[i]\)表示向右的最大长度即可
当然也可以用并查集维护,每次路径压缩时把所有改的节点记录到栈中

#include<bits/stdc++.h>
using namespace std;

const int N=5e4+5;
int n,m,S,L[N],R[N],a[N],ans[N],bl[N],top,Ans;
struct A{int l,r,i; }q[N],st[N];

bool cmp(A i,A j) {
	return bl[i.l]==bl[j.l]?i.r<j.r:i.l<j.l;
}

inline void add(int x) {
	L[x]=L[x-1]+1,R[x]=R[x+1]+1;
	R[x-L[x]+1]=R[x]+L[x]-1;
	L[x+R[x]-1]=R[x]+L[x]-1;
	Ans=max(Ans,R[x]+L[x]-1);
}

inline void Add(int x,int i) {
	st[++top]=(A){L[x],R[x],x};
	L[x]=L[x-1]+1,R[x]=R[x+1]+1;
	st[++top]=(A){L[x-L[x]+1],R[x-L[x]+1],x-L[x]+1};
	R[x-L[x]+1]=R[x]+L[x]-1;
	st[++top]=(A){L[x+R[x]-1],R[x+R[x]-1],x+R[x]-1};
	L[x+R[x]-1]=R[x]+L[x]-1;
	ans[i]=max(ans[i],R[x]+L[x]-1);
}

int main() {
	freopen("permu.in","r",stdin);
	freopen("permu.out","w",stdout);
	scanf("%d%d",&n,&m); S=sqrt(n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		bl[i]=(i-1)/S+1;
	}
	int c=0;
	for(int i=1;i<=m;i++) {
		int l,r;  scanf("%d%d",&l,&r);
		if(bl[l]==bl[r]) {
			Ans=0;
			for(int j=l;j<=r;j++) {
				add(a[j]);
			}
			ans[i]=Ans;
			for(int j=l;j<=r;j++) {
				L[a[j]]=R[a[j]]=0;
			}
		} else {
			q[++c]=(A){l,r,i};
		}
	}
	sort(q+1,q+c+1,cmp);
	int r=0;
	for(int i=1;i<=c;i++) {
		if(bl[q[i-1].l]!=bl[q[i].l]) {
			r=bl[q[i].l]*S;
			memset(L,0,sizeof(L));
			memset(R,0,sizeof(R));
			Ans=0;
		}
		while(r<q[i].r) r++,add(a[r]);
		top=0; ans[q[i].i]=Ans;
		for(int j=bl[q[i].l]*S;j>=q[i].l;j--) {
			Add(a[j],q[i].i);
		}
		while(top) {
			L[st[top].i]=st[top].l;
			R[st[top].i]=st[top].r;
			top--;
		}
	}
	for(int i=1;i<=m;i++) {
		printf("%d\n",ans[i]);
	}
	return 0;
}
posted @ 2021-04-01 16:05  wwwsfff  阅读(52)  评论(0编辑  收藏  举报