[Tkey] 生日礼物

题意简述

彩珠有 \(n\)\(k\) 种,每个珠子都有一个坐标 \(p_{i}\),求最小的区间长度,使得这个区间包含全部的 \(k\) 种彩珠.

分析

发现我们可以维护每一种颜色的最近出现坐标.

因为是最近的出现坐标,所以离现在的距离(即答案)一定是更优的,那么我们用这个值来更新答案一定就是最优的.

假如我们想要让当前值更新答案,那么我们就需要让全部的颜色都在区间里出现过,也就是去寻找全部颜色的出现坐标的最小值,然后用当前坐标去减就行了.

突然发现能用线段树建在 \(k\) 上来搞这东西,而且它的查询是O(1)的,简直是太快了,膜拜了.

然后就秒了.

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct node{
	int id,color;
	bool operator <(const node &A)const{
		return id<A.id;
	}
}a[1000001];
struct tree{
	int l,r;
	int min;
}t[241];
#define tol (id*2)
#define tor (id*2+1)
#define mid(l,r) mid=((l)+(r))/2
void build(int id,int l,int r){
	t[id].l=l;t[id].r=r;t[id].min=-1;
	if(l==r){
		return;
	}
	int mid(l,r);
	build(tol,l,mid);
	build(tor,mid+1,r);
}
void change(int id,int p,int val){
	if(t[id].l==t[id].r){
		t[id].min=val;return;
	}
	if(p<=t[tol].r) change(tol,p,val);
	else change(tor,p,val);
	t[id].min=min(t[tol].min,t[tor].min);
}
int ask(){
	return t[1].min;
}
int main(){
	int cnt=0;
	ios::sync_with_stdio(false);
	cin>>n>>k;int x,y;
	for(int i=1;i<=k;++i){
		cin>>x;
		while(x--){
			cin>>y;
			a[++cnt]={y,i};
		}
	}
	build(1,1,k);
	sort(a+1,a+n+1);int ans=0x7fffffff;
	for(int i=1;i<=n;++i){
//		cout<<i<<" color : "<<a[i].color<<" id: "<<a[i].id<<endl;
		change(1,a[i].color,a[i].id);
		if(ask()!=-1){
//			cout<<"can "<<ask()<<" "<<a[i].id-ask()<<endl;
			ans=min(ans,a[i].id-ask());
		}
	}
	cout<<ans;
}
posted @ 2024-06-09 17:21  HaneDaniko  阅读(16)  评论(0编辑  收藏  举报
/**/