【题解】Luogu P1638 逛画展 & Luogu P2564 [SCOI2009] 生日礼物
两道基本一样的题。
思路
考虑维护双指针 \(l\)、\(r\),表示当前区间的两端点。
向后拓展 \(r\) 直到 \(m\) 个元素都包含在区间内。
接着考虑继续向后拓展 \(r\) 时如何保证在当前 \(r\) 固定的情况下区间合法且最小。
在整个拓展过程中,遇到重复元素,意味着此前出现过该元素的位置可以不被包含在区间内。
维护一个数组 \(p\),记录第 \(i\) 个元素出现的最后位置 \(p_i\)。在 \(r\) 向后拓展的过程中,判断 \(l\) 所在位置是否还需要保留在区间内,即 \(p_{a_l}\) 是否等于 \(l\)。如果等于,则 \(l\) 不变。如果大于,则 \(l\) 向后移动,继续判断下一项直到不能再向后移动。记录 \(m\) 个元素都包含在区间后对于每个 \(r\) 的区间信息,选择长度最小的区间输出。
时间复杂度 \(O(n)\)。
实现
P1638 逛画展
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int M=2e3+10;
int n,m,cnt,l=1;
int ansl=1e9,ansr=2e9;
int a[N],p[M];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
memset(p,-1,sizeof(p));
for(int i=1;i<=n;i++){
cin>>a[i];
if(p[a[i]]==-1) cnt++;
p[a[i]]=i;
while(l<i&&l<p[a[l]]) l++;
if(cnt==m&&i-l<ansr-ansl) ansr=i,ansl=l;
}
cout<<ansl<<' '<<ansr;
return 0;
}
P2564 [SCOI2009] 生日礼物
位置值域来到了 \(2^{31}\) 且一个位置可存在多个元素。使用结构体记录元素位置,按位置排序即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int K=100;
int n,k,l=1;
int cnt,minl=INT_MAX;
int p[K],tot;
struct Node{
int tpe,pos;
}a[N];
bool cmp(Node x,Node y){
return x.pos<y.pos;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=1;i<=k;i++){
int t;
cin>>t;
for(int j=1;j<=t;j++){
int x;
cin>>x;
a[++tot].tpe=i;
a[tot].pos=x;
}
}
sort(a+1,a+1+n,cmp);
memset(p,-1,sizeof(p));
for(int i=1;i<=n;i++){
if(p[a[i].tpe]==-1) cnt++;
p[a[i].tpe]=a[i].pos;
while(l<i&&a[l].pos<p[a[l].tpe]) l++;
if(cnt==k&&a[i].pos-a[l].pos<minl) minl=a[i].pos-a[l].pos;
}
cout<<minl;
return 0;
}

浙公网安备 33010602011771号