[洛谷P2898][USACO08JAN]haybale猜测Haybale Guessing

题目←

题意:

总共有n个互不相同的正整数,LYK每次猜一段区间的最小值。形如[li,ri]这段区间的数字的最小值一定等于xi。
我们总能构造出一种方案使得LYK满意。直到…… LYK自己猜的就是矛盾的!
例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,这显然就是矛盾的。
你需要告诉LYK,它第几次猜数字开始就已经矛盾了。

105~106数据范围,容易考虑nlogn
这个log就来自二分了……

1、一段区间被最小值比他大的区间完全包含的时候
2、n个数互不相同,所以当两个无交集的区间最小值相同时一定矛盾

我们发现,当遇到一段最小值比当前区间小的区间时,只要不被当前区间完全包含就构不成矛盾情况
而当前区间能更新最小值的部分也仅限于不被最小值较小的区间包含的部分

所以按最小值从大到小处理,这样就不用划分分区间了……
用并查集维护区间染色情况
因为从大到小处理,之前染过的颜色一定比当前最小值大
故发现当前区间被完全染色时一定矛盾

至于情况二,只会在最小值相同的猜测中出现
而排了序之后他们是相邻的
到时候判断一下交集就好了

注意最小值相同的区间的处理,合并时合并最大范围,判断时最小范围(实际存在区间)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 2000000 + 50;
int fa[MAXN],rank[MAXN];
int find(int x){
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
struct px{
	int l,r,z;
}B[MAXN << 1],t[MAXN << 1];
inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
	for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
int n,m;
bool cmp(px a,px b){
	return a.z > b.z;
}
bool solve[MAXN];
bool check(int k){
	for(int i = 1;i <= n + 1;i ++){//会访问到fa[n + 1] 
		fa[i] = i;
	}
	for(int i = 1;i <= k;i ++){
		t[i] = B[i];
	}
	sort(t + 1,t + k + 1,cmp);
	int l,r,liml,limr;
	l = liml = t[1].l;
	r = limr = t[1].r;
	t[k + 1].l = 0;
	t[k + 1].r = n + 1;
	for(int i = 2;i <= k + 1;i ++){
		while(t[i].z == t[i - 1].z){
			l = min(l,t[i].l);
			r = max(r,t[i].r);
			liml = max(liml,t[i].l);
			limr = min(limr,t[i].r);
			if(liml > limr)return false;
			i ++;
			if(i > k)break;
		}
		if(find(liml) > limr)return false;
		for(int j = l;j <= r;j ++){
			j = find(j);
			fa[j] = find(r + 1);
		}
		l = liml = t[i].l;
		r = limr = t[i].r;
	}
	return true;
}
int L = 1,R;
int main(){
	n = read();
	m = read();
	for(int i = 1;i <= m;i ++){
		B[i].l = read();
		B[i].r = read();
		B[i].z = read();
	}
	R = m + 1;
	while(R - L > 1){
		int mid = L + R >> 1;
		if(check(mid))L = mid;
		else R = mid;
	}
	if(R == m + 1)R = 0;
	printf("%d",R);
}

这题考试时限是2s,但洛谷只有1s……
其实区别在这一段

for(int j = l;j <= r;j ++){
			j = find(j);
			fa[j] = find(r + 1);
		}

std代码是

for(j = find(lmin); j <= rmax; j++)
				f[find(j)] = find(rmax + 1);

多跑了很多已经合并过的区间

posted @ 2017-11-01 18:17  _平行  阅读(242)  评论(1编辑  收藏  举报