[USACO11NOV]牛的阵容Cow Lineup

【问题描述】

农民约翰雇一个专业摄影师给他的部分牛拍照。由于约翰的牛有好多品种,他喜欢他的照片包含每

个品种的至少一头牛。

约翰的牛都站在一条沿线的不同地方, 每一头牛由一个整数位置 X_i以及整数品种编号 ID_i表示。

约翰想拍一张照片,这照片由沿线的奶牛的连续范围组成。照片的成本与规模相当,这就意味着,在一

系列照片中的最大和最小 X 坐标的差距决定了照片的成本。

请帮助约翰计算最小的照片成本,这些照片中有每个不同的品种的至少一头牛,没有两头牛愿意站

在同一个地点的。

【输入格式】

第 1 行:牛的数量 N;

第 2..1+N 行:每行包含 2 个以空格分隔的正整数 X_i 和 ID_i;意义如题目描述;

【输出格式】

输出共一行,包含每个不同品种 ID 的照片的最低成本。

【输入样例】

6 25 7 26 1 15 1 22 3 20 1 30 1

【输出样例】

4

【输入说明】

在不同的坐标点 25,26,15,22,20,30 中有六头牛

【输出说明】

在约翰的牛中,从 X=22 到 X=26(整个规模为 4)包含了每个的不同品种的 ID 3,7 和 1。

【数据规模】

对于 50%的数据: 1≤N≤300;

对于 100%的数据:1≤N≤50,000;0≤X_i≤1,000,000,000;1≤ID_i≤1,000,000,000;

刚刚看到这一题,觉得不是很难,但是一做起来,就感觉束手无策,看了一下数据范围,我感觉做法应该是O(n)或者是O(n^logn)

因为这一道题必须要排序,所以直接排除O(n)的做法

因为品种数量很大,所以要给di离散一下,可以省掉map

我想了想,决定用两个指针,分别指向左右两端,左指针for循环,右指针指向刚刚好能满足条件的那个点,并且多次记录最大值

查找时间为O(n),加上快排O(nlogn)

也可能是我看到xi<=10^9的原因吧,我竟然用了一个二分,查找时间是O(30n),因为30是常数所以时间还是O(N),

代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while( isdigit(s))x=(x<<1)+(x<<3)+s-48,s=getchar();
    return !f?x:-x;
}
const int N=5e4+20;
struct node{
    int x,d;
    inline bool operator<(const node &k)const{
        return x<k.x;
    }
}a[N];int n;
struct LSnode{
    int x,z,p;
    inline bool operator<(const LSnode &k)const{
        return x<k.x;
    }
}b[N];
int bk[N];
inline bool check(int x){
    memset(bk,0,sizeof(bk));
    int p2=1,ans=1;bk[a[1].d]=1;
    for(int p1=1;p1<=n;p1++){
        if(p2==n)break;	
        while(a[p2+1].x-a[p1].x<=x&&p2<n){
            p2++;bk[a[p2].d]++;
            if(bk[a[p2].d]==1)ans++;
        }
        if(ans==b[n].z)return 1;
        bk[a[p1].d]--;
        if(!bk[a[p1].d])ans--;
    }
    return 0;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i].x=read();a[i].d=read();
        b[i].x=a[i].d;b[i].p=i;
    }
    sort(b+1,b+n+1);//离散化 
    b[1].z=1;
    for(int i=2;i<=n;i++)b[i].z=b[i-1].z+(b[i].x==b[i-1].x?0:1);
    for(int i=1;i<=n;i++)a[b[i].p].d=b[i].z;
    sort(a+1,a+n+1);
    int l=0,r=1e9,mid,ans=-1;//二分 
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

打完二分以后突然发现里面的check似乎可以直接用来求最小值,于是我又打了一个不用二分的程序

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=0;char s=getchar();
	while(!isdigit(s))f|=s=='-',s=getchar();
	while( isdigit(s))x=(x<<1)+(x<<3)+s-48,s=getchar();
	return !f?x:-x;
}
const int N=5e4+20;
struct node{
	int x,d;
	inline bool operator<(const node &k)const{
		return x<k.x;
	}
}a[N];int n;
struct LSnode{
	int x,z,p;
	inline bool operator<(const LSnode &k)const{
		return x<k.x;
	}
}b[N];
int bk[N];
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i].x=read();a[i].d=read();
		b[i].x=a[i].d;b[i].p=i;
	}
	sort(b+1,b+n+1);
	b[1].z=1;
	for(int i=2;i<=n;i++)b[i].z=b[i-1].z+(b[i].x==b[i-1].x?0:1);
	for(int i=1;i<=n;i++)a[b[i].p].d=b[i].z;
	sort(a+1,a+n+1);
	int p2=1,ans=1;bk[a[1].d]=1;
	int minn=1e9+10;
	for(int p1=1;p1<=n;p1++){
		while(p2<n&&ans<b[n].z){
			p2++;bk[a[p2].d]++;
			if(bk[a[p2].d]==1)ans++;
		}
		if(ans<b[n].z)break;
		minn=min(minn,a[p2].x-a[p1].x);
		bk[a[p1].d]--;
		if(!bk[a[p1].d])ans--;
	}
	printf("%d\n",minn);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zsyzClb/article/details/84672077
今日推荐