P2564 [SCOI2009]生日礼物
看到最短容易想到二分答案,然后发现尺取法还更好搞
动态维护左右端点 $l,r$,维护 $p[i]$ 表示颜色 $i$ 最后一次出现的位置,$cnt$ 维护当前有几种不同颜色
随着 $l$ 增大,显然它的最小的 $r$ 是不降的,所以枚举 $l$,动态右移 $r$ ,维护 $p$ 并维护 $cnt$
因为同种颜色位置不同所以不用离散化,否则要记得离散化
复杂度 $O(n)$
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e6+7,INF=1e9+7; struct dat{ int pos,col; inline bool operator < (const dat &tmp) const { return pos<tmp.pos; } }d[N]; int n,m,p[N],ans=INF; int main() { n=read(),m=read(); int tot=0,a; for(int i=1;i<=m;i++) { a=read(); while(a--) d[++tot].pos=read(),d[tot].col=i; } sort(d+1,d+n+1); int l=1,r=0,cnt=0; while(l<=n) { while(cnt<m&&r<=n)//对于当前的l找到最近的r { r++; if(!p[d[r].col]) cnt++;//维护cnt p[d[r].col]=d[r].pos;//维护p } if(r>n) break; ans=min(ans,d[r].pos-d[l].pos); if(p[d[l].col]==d[l].pos) p[d[l].col]=0,cnt--;//如果当前颜色是最左边的颜色才要考虑更新 l++; } printf("%d",ans); return 0; }