P2564 [SCOI2009]生日礼物
题意:
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;
}