一个和官方题解不大一样的做法,常数略微大一点点,故作文记之。
首先第一点,每一个区间都有可能产生若干个冲突对,然后显然我选择的区间一定要覆盖这些冲突。但是不一定要全部覆盖,其实留出每组冲突对的最左边或者最右边都可以。以及可以感性理解一下,假如区间的左端点向右移动,最优答案的右端点也会向右移动。
现在就是把这个问题拆成一下几个问题:1、每个区间最多能缩多少 2、枚举开头之后最少需要多少能够满足限制
首先如果确定左端点,那么答案的右端点就是每个区间的必要的右端点取 max ,因此先分别考虑每一个区间,如果答案的左端点在一个区间的左端点的左边,那么显然对于这个区间而言对答案的右端点的限制是不会变得,只有当答案的左端点越过了这个区间的左端点时,这个区间对答案的右端点的限制就会向右挪
因此可以从前往后扫一遍,开一个 last[i] 表示扫到某个位置,值 i 最后一次出现在哪,然后扫到一个数就用 last[a[i]] 来更新 rmost ,然后更新 last[a[i]] ,然后可以用一个堆来记着区间,边扫边加区间,之后按照右端点排序,每次检查堆顶,每个区间在出堆的时候被 rmost 更新,然后就可以跑出每个区间在答案左端点比自身左端点小的时候最少要拓展到哪里,记为 must[i]
然后再考虑在向右挪答案的左端点的时候,怎么更新右端点。首先先考虑一个性质,假如现在做到第 i 位,现在覆盖在上面的有两个区间甲乙,甲右端点比乙的大,那么当我把答案左端点从 i 挪到 i+1 时,甲对答案右端点产生的限制一定比乙更右边,因为挪到下一位之后我会把答案更新成某个 last[a[i]] ,而甲右端点的 last[a[i]] 一定比乙的大,因此我只需要在挪答案左端点时记住目前走过的区间中右端点最靠右的是多少即可。
代码
// Problem: F. Diverse Segments
// Contest: Codeforces - Codeforces Round #792 (Div. 1 + Div. 2)
// URL: https://codeforces.com/problemset/problem/1684/F
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// Powered by CP Editor (https://github.com/cpeditor/cpeditor)
#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch))f^=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?x:-x;
}
const int N=2e5+5;
int a[N],n,m,rks,ans,lmost,now,must[N],last[N];
pair<int,int> b[N],mmt[N];
vector<pair<int,int>> seg[N],rev[N];
priority_queue<pair<int,int>> q;
void qclear(){
while(q.size())q.pop();
}
void gao1(){
qclear();
int need=n+1;
lmost=need;
memset(last,0,sizeof(int)*(rks+5));
for(int i=n;i>=1;--i){
for(auto x:rev[i]){
q.push(x);
}
if(last[a[i]])need=min(need,last[a[i]]);
last[a[i]]=i;
while(q.size()&&(q.top().fi==i)){
int idx=q.top().se;
q.pop();
must[idx]=need;
}
}
for(int i=1;i<=m;++i){
if(must[i]<=mmt[i].se)lmost=min(lmost,must[i]);
}
}
void gao2(){
qclear();
int need=0;
memset(last,0,sizeof(int)*(rks+5));
memset(must,0,sizeof(int)*(rks+5));
for(int i=1;i<=n;++i){
for(auto x:seg[i]){
q.push({-x.fi,x.se});
}
if(last[a[i]])need=max(need,last[a[i]]);
last[a[i]]=i;
while(q.size()&&(-q.top().fi==i)){
int idx=q.top().se;
q.pop();
must[idx]=need;
}
}
}
void extend(int x){
while(now<=x){
last[a[now]]=now;
++now;
}
}
void gao3(){
memset(last,0,sizeof(int)*(rks+5));
int extd=0;
ans=1e9;
now=1;
for(int i=1;i<=m;++i){
extd=max(extd,must[i]);
}
for(int i=1;i<=min(n,lmost);++i){
if(i>1){
extd=max(extd,last[a[i-1]]);
}
extd=max(extd,i);
int rf=0;
for(auto x:seg[i]){
extd=max(extd,must[x.se]);
rf=max(rf,x.fi);
}
extend(rf);
ans=min(ans,extd-i+1);
}
}
void solve(){
n=read(),m=read(),rks=0;
for(int i=1;i<=n;++i){
seg[i].clear();
rev[i].clear();
a[i]=read();
b[i]={a[i],i};
}
sort(b+1,b+n+1);
a[b[1].se]=++rks;
for(int i=2;i<=n;++i){
if(b[i].fi==b[i-1].fi)a[b[i].se]=a[b[i-1].se];
else a[b[i].se]=++rks;
}
for(int i=1;i<=m;++i){
int l=read(),r=read();
mmt[i]={l,r};
seg[l].push_back({r,i});
rev[r].push_back({l,i});
must[i]=n+1;
}
gao1();
if(lmost==n+1){
puts("0");
return;
}
gao2();
gao3();
printf("%lld\n",ans);
}
signed main(){
int T=read();
while(T--)
solve();
return 0;
}