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)都满足题意。
因而对于每一个左端点lll,需要找到最小的一个右端点rrr
单调性:对于每一个左端点lll,当左端点右移(增大)的过程中,右端点也一定右移(增大)
有了上述单调性考虑双指针表示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,pl)[1,pl)[1,pl)内部不存在逆序对,以及(pr,x](pr,x](pr,x]内部不存在逆序对,因而只需要让指针iii在[1,pl][1,pl][1,pl]之间,而指针jjj在[pr,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;
}
要加油哦~