题意:
n个彩珠,k个种类,分布在一个彩带上,现在要选取彩带的一部分,要求该部分包含所有种类的彩珠,且长度尽可能短,你能计算这个最短的长度吗?
1≤N≤1000000,1≤K≤60,0≤珠子位置<2{31}
题解:
比赛时第一反应是尺取,但是一看这个距离放弃了,后来想可以先离散?或者map距离,因为种种原因都没做出,现在赛后补题
实质用的是区间伸缩
因为珠子的位置范围很大,但是珠子的数量有限,所以我们完全可以枚举珠子的数量
我们给每种彩珠编号以及存下每种彩珠的坐标
然后根据坐标排序
然后就是区间伸缩(我一直认为是尺取),两个指针l和r,分别指向左右两端的两种珠子,在一半题中我们都会让指针指向距离的左右两端,但是因为本题距离过长,所以我们使其指向左右两端彩珠(即第i个彩珠和第j个彩珠)
左端l更新情况是:如果第l个彩珠和第l-1个彩珠在一个位置,那我们就l++,以l+1个彩珠为新的起点
相当于我们以不同的彩珠作为左端点,然后枚举右端点,当num == m时,说明目前这个区间上有了所有的彩珠
(u1s1我也将不大很明白,就是把尺取距离变成尺取每种彩珠)
详细看看代码把
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
return s*w;
}
const int maxn=1000008;
struct node{
int pos;//位置
int id;//种类
}a[maxn];
int b[maxn];//用来统计每种彩珠的出现次数
bool cmp(node a,node b)
{
return a.pos<b.pos;
}
int main()
{
int n,k;
cin>>n>>k;
int tot=0;
for(int i=1;i<=k;i++){
int t;
cin>>t;
for(int j=1;j<=t;j++){
cin>>a[++tot].pos;
a[tot].id=i;
}
}
sort(a+1,a+1+tot,cmp);
int l=1,r=1;
int num=0;//记录当前彩带上有多少种彩珠
b[a[l].id]++;//先将第一个彩珠记录
num++;
int sum=1e9;//最后记录答案
while(l<=n&&r<=n)
{
if(num==k)
{
sum=min(sum,a[r].pos-a[l].pos);//更新答案
b[a[l].id]--;
if(b[a[l].id]==0)num--;//离开l点
l++;
if(l>n)break;
while(a[l].pos==a[l-1].pos)//如果移动后还是原来那个位置
{
b[a[l].id]--;
if(b[a[l].id]==0)num--;
l++;
if(l>n)break;
}
}
else
{
r++;
if(r>n)break;
b[a[r].id]++;
if(b[a[r].id]==1)num++;
while(a[r].pos==a[r+1].pos)
{
r++;
if(r>n)break;
b[a[r].id]++;
if(b[a[r].id]==1)num++;
}
}
}
cout<<sum;
}