【题解】Luogu P11570 「chaynOI R1 T3」镍铬合金机器人
很巧的 trick。
首先离线。从大到小扫 \(l\),维护数组 \(p_i\) 表示当前出现 \(i\) 的最小的位置。
显然当确定了左端点,从左到右的 \(\operatorname{mex}\) 是单调不降的。因此我们要求的就是一段区间 \([l',r']\),满足 \(\operatorname{mex}[l,l']\ge x\) 且 \(l'\) 最小,\(\operatorname{mex}[l,r']\le y\) 且 \(r'\) 最大。(其中 \(\operatorname{mex}[l,r]\) 表示区间 \([l,r]\) 的 \(\operatorname{mex}\)。)显然 \(l'=\max_{0\le i<x}p_i\),\(r'+1=\max_{0\le i\le y}p_i\)。用线段树维护 \(p\) 即可。时间复杂度 \(O(n\log n)\)。代码实现时要注意一些细节。
#include<cstdio>
#include<iostream>
#include<vector>
#define ll long long
#define il inline
#define pb push_back
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=3e5+5;
int n,m,a[maxn],ans[maxn];
struct wen{
int x,y,id;
};
vector<wen> wt[maxn];
int tr[maxn<<2];
il void pushup(int id){
tr[id]=max(tr[lid],tr[rid]);
}
il void build(int id,int l,int r){
tr[id]=n+1;
if(l==r){
return ;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
}
il void upd(int id,int l,int r,int p,int v){
if(l==r){
tr[id]=v;
return ;
}
int mid=(l+r)>>1;
if(p<=mid){
upd(lid,l,mid,p,v);
}
else{
upd(rid,mid+1,r,p,v);
}
pushup(id);
}
il int query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return tr[id];
}
int mid=(L+R)>>1,res=0;
if(l<=mid){
res=max(res,query(lid,L,mid,l,r));
}
if(r>mid){
res=max(res,query(rid,mid+1,R,l,r));
}
return res;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1,l,x,y;i<=m;i++){
cin>>l>>x>>y;
wt[l].pb((wen){x,y,i});
}
build(1,0,n);
for(int i=n;i;i--){
upd(1,0,n,a[i],i);
for(wen j:wt[i]){
int id=j.id,x=j.x,y=j.y;
ans[id]=query(1,0,n,0,y)-(x?query(1,0,n,0,x-1):i);
}
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}
}
int main(){return asbt::main();}

浙公网安备 33010602011771号