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;
}

 

posted @ 2019-03-24 11:52  LLTYYC  阅读(183)  评论(0编辑  收藏  举报