E. Range Deleting
首先不难知道如果 f ( l , r ) f(l,r) f(l,r)满足题意,那么 f ( l , r + 1 ) , f ( l , r + 2 ) , … , f ( l , x ) f(l,r+1),f(l,r+2),\dots,f(l,x) f(l,r+1),f(l,r+2),…,f(l,x)都满足题意。
因而对于每一个左端点 l l l,需要找到最小的一个右端点 r r r
单调性:对于每一个左端点 l l l,当左端点右移(增大)的过程中,右端点也一定右移(增大)
有了上述单调性考虑双指针表示 f ( i , j ) f(i,j) f(i,j)
预处理出以下数组
①:l[i]
值是i
的最小数组下标
②:r[i]
值是i
的最大数组下标
③:ll[i]
值是i~x
的最小数组下标
④:rr[i]
值是1~i
的最大数组下标
对于一个区间 [ l , r ] [l,r] [l,r],假设值在 [ 1 , l − 1 ] [1,l-1] [1,l−1]以及 [ r + 1 , x ] [r+1,x] [r+1,x]内部不存在逆序对,只需要判断 [ 1 , l − 1 ] [1,l-1] [1,l−1]和 [ r + 1 , x ] [r+1,x] [r+1,x]之间是否存在逆序对即值在 [ 1 , l − 1 ] [1,l-1] [1,l−1]最大数组下标是否大于 [ r + 1 , x ] [r+1,x] [r+1,x]最小数组下标即如果存在逆序对(rr[l-1]<ll[r+1]
)不满足条件,只需根据此调整双指针位置。
对于假设条件,显然可以求出对于 [ 1 , p l ) [1,pl) [1,pl)内部不存在逆序对,以及 ( p r , x ] (pr,x] (pr,x]内部不存在逆序对,因而只需要让指针 i i i在 [ 1 , p l ] [1,pl] [1,pl]之间,而指针 j j j在 [ p r , x ] [pr,x] [pr,x]之间即可。
#define IO ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
#pragma GCC optimize(2)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
constexpr int N=1000010;
int a[N],n,x;
int l[N],r[N],ll[N],rr[N];
int main()
{
IO;
cin>>n>>x;
memset(l,0x3f,sizeof l);
memset(ll,0x3f,sizeof ll);
memset(r,-1,sizeof r);
memset(rr,-1,sizeof rr);
for(int i=1;i<=n;i++)
{
cin>>a[i];
l[a[i]]=min(l[a[i]],i);
r[a[i]]=max(r[a[i]],i);
}
// rr[i] 1~i最右边的位置
// ll[i] i~x最左边的位置
for(int i=1;i<=x;i++) rr[i]=max(rr[i-1],r[i]);
for(int i=x;i>=1;i--) ll[i]=min(ll[i+1],l[i]);
long long res=0;
// [1,pl) 以及 (pr,x] 不存在逆序对
int pl=1,pr=x;
while(pl<=x&&rr[pl-1]<=l[pl]) pl++;
while(pr>=1&&ll[pr+1]>=r[pr]) pr--;
for(int i=1,j=pr;i<=pl;i++)
{
while(j<=x&&(j<i||rr[i-1]>=ll[j+1])) j++;
res+=x-j+1;
}
cout<<res<<'\n';
return 0;
}
要加油哦~