Solution
显然每次操作是要选一个上升子序列, 然后把整体往后推,去掉一个最大值,加入
A
A
A,考虑怎么维护。
看见数据范围和时限,大胆猜测是分块。这里采用一种类似线段树中
l
a
z
y
lazy
lazy标记的思想。对于中间的整块,显然是加入
A
A
A之后出来一个最大值,我们每个块用一个堆维护最大值,暂时不管数的顺序。但是对于左右的不完整块,我们必须知道数的位置,用另外一个堆维护这个块有哪些数经过过,然后当我们需要知道它的位置信息时就把他们拿出来重构,具体方法见代码。
复杂度
O
(
n
log
n
n
)
O(n\log n\sqrt n)
O(nlognn)。
Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=400010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,k,bel[Maxn],st[Maxn],ed[Maxn],a[Maxn];
priority_queue<int>q1[640];
priority_queue<int,vector<int>,greater<int> >q2[640];
void rebuild(int x)
{
while(!q1[x].empty())q1[x].pop();
for(int i=st[x];i<=ed[x];i++)q1[x].push(a[i]);
}
void update(int x)
{
if(q2[x].empty())return;
for(int i=st[x];i<=ed[x];i++)
{
int v=q2[x].top();
if(v<a[i])swap(a[i],v),q2[x].pop(),q2[x].push(v);
}
while(!q2[x].empty())q2[x].pop();
rebuild(x);
}
int solve(int l,int r,int A)
{
if(bel[l]==bel[r])
{
update(bel[l]);
for(int i=l;i<=r;i++)
if(A<a[i])swap(A,a[i]);
rebuild(bel[l]);
}
else
{
update(bel[l]);
for(int i=l;i<=ed[bel[l]];i++)
if(A<a[i])swap(A,a[i]);
rebuild(bel[l]);
for(int i=bel[l]+1;i<bel[r];i++)
{
if(q1[i].top()<=A)continue;
int nA=q1[i].top();q1[i].pop();
q1[i].push(A);q2[i].push(A);
A=nA;
}
update(bel[r]);
for(int i=st[bel[r]];i<=r;i++)
if(A<a[i])swap(A,a[i]);
rebuild(bel[r]);
}
return A;
}
int main()
{
n=read(),m=read();k=(int)sqrt(n);
for(int i=1;i<=n;i++)
{
bel[i]=(i-1)/k+1,q1[bel[i]].push(a[i]=read());
ed[bel[i]]=i;if(!st[bel[i]])st[bel[i]]=i;
}
while(m--)
{
int l=read(),r=read(),A=read();
if(l<=r)printf("%d\n",solve(l,r,A));
else printf("%d\n",solve(1,r,solve(l,n,A)));
}
}