POJ Haybale Guessing

Description

The cows, who always have an inferiority complex about their intelligence, have a new guessing game to sharpen their brains.

A designated 'Hay Cow' hides behind the barn and creates N (1 ≤ N ≤ 1,000,000) uniquely-sized stacks (conveniently numbered 1..N) of hay bales, each with 1..1,000,000,000 bales of hay.

The other cows then ask the Hay Cow a series of Q (1 ≤ Q ≤ 25,000) questions about the the stacks, all having the same form:

What is the smallest number of bales of any stack in the range of stack numbers  Q l.. Qh (1 ≤  Q l ≤  NQ l ≤  Qh ≤  N)?

The Hay Cow answers each of these queries with a single integer A whose truthfulness is not guaranteed.

Help the other cows determine if the answers given by the Hay Cow are self-consistent or if certain answers contradict others.

Input

* Line 1: Two space-separated integers: N and Q
* Lines 2..Q+1: Each line contains three space-separated integers that represent a single query and its reply: QlQh, and A

题目大意

有一串长度为n (1 ≤  n  ≤ 1,000,000) 的数串(其中每个数字只出现一次)。有 Q  (1 ≤  Q  ≤ 25,000) 个命令,每个命令格式为:li ri ai.
 表示[l,r]这个区间内,最小的数为ai,问第几个命令最先和之前的命令冲突。题目有多组数据。

Output

* Line 1: Print the single integer 0 if there are no inconsistencies among the replies (i.e., if there exists a valid realization of the hay stacks that agrees with all Q queries). Otherwise, print the index from 1..Q of the earliest query whose answer is inconsistent with the answers to the queries before it.

Sample Input

20 4
1 10 7
5 19 7
3 12 8
11 15 12

Sample Output

3

题解

USACO金组的题果然不一般。感谢lwh大神,在他的指导下才会做这道题。

首先,我最直观的想法是在线用线段树,可是仔细想下去,根本没法写出来。

在lwh大神的指导下,知道了这题可离线做。正解为二分答案+离散化。

首先我们可以推出一个结论:假设一段[x,y]中的最小值为z,那么一段数[a,b](x<=a<=y,x<=b<=y,a<=b)的最小值c一定满足c>=z。

所以,如果有一段命令,他们能合并成一段数[x,y],最小值为z。那么若一段数[a,b](x<=a<=y,x<=b<=y,a<=b)的最小值c<z。则这满足冲突关系。

也就是说。我们可以二分所给的命令(答案为mid),并将1~mid的命令的“最小值”降序排序,则最后加入的命令在已经被覆盖的区间内,则满足冲突关系,继续二分即可。区间覆盖用线段树很简单。

但n很大,所以要离散,将所给区间端点从小到大排序即可。注意 一个问题,离散化后的相邻的两个值可能离散化前不相邻,那么覆盖了这两个端点后其实没有覆盖这个区间。

具体见代码:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m;
struct wen {int l,r,v;} q[25002]/*命令描述*/,now[25002]/*命令排序*/;
struct dian {int v,id;} d[50002];/*离散用的*/
struct shu {int l,r;bool g;} tr[650002];/*线段树*/
bool cmp(const dian &x,const dian &y)   {return x.v<y.v;}/*点排序*/

bool kp(const wen &x,const wen &y)   {return x.v>y.v;}/*命令排序*/

void build(int w,int b,int e)//建树 
{
	tr[w].l=b; tr[w].r=e; tr[w].g=0;
	if(b==e) return;
	int mid=(b+e)>>1;
	build(w<<1,b,mid);build((w<<1)+1,mid+1,e);
}
void init()//输入并处理 
{
	for(int i=1;i<=m;i++)
	   {scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].v);
	    if(q[i].l>q[i].r) swap(q[i].l,q[i].r);
	    d[i*2].v=q[i].l; d[i*2].id=i*2;
	    d[i*2+1].v=q[i].r; d[i*2+1].id=i*2+1;
	   }
	sort(d+2,d+m+m+2,cmp);
	d[1].v=-0x7fffffff;
	int lon=0;
	for(int i=2;i<=m+m+1;i++)
	   {if(d[i].v!=d[i-1].v)
	       {lon++;
		    if(d[i].v!=d[i-1].v+1) lon++;//为了避免题解中提到的离散中的问题 
		   }
		if(d[i].id&1) q[d[i].id/2].r=lon;
		else q[d[i].id/2].l=lon;
	   }
}
void down(int w)
{
	if(tr[w].g==1) {tr[w<<1].g=tr[(w<<1)+1].g=1;}
}
bool ask(int w,int b,int e)
{
	if(tr[w].l==b&&tr[w].r==e) return tr[w].g;
	int mid=(tr[w].l+tr[w].r)>>1;
	down(w);
    if(e<=mid) return ask(w<<1,b,e);
    else if(mid+1<=b) return ask((w<<1)+1,b,e);
    else
	   {bool fla=1;
        if(ask(w<<1,b,mid)==0) fla=0;
        if(ask((w<<1)+1,mid+1,e)==0) fla=0;
        return fla;
       }
}

void insert(int w,int b,int e)
{
    if(tr[w].l==b&&tr[w].r==e)
       {tr[w].g=1;return ;}
    down(w);
    int mid=(tr[w].l+tr[w].r)>>1;
    if(e<=mid) insert(w<<1,b,e);
    else if(mid+1<=b) insert((w<<1)+1,b,e);
    else {insert(w<<1,b,mid);insert((w<<1)+1,mid+1,e);}
    if(tr[w<<1].g&&tr[(w<<1)+1].g) tr[w].g=1;
    else tr[w].g=0;
}

bool check(int wz)
{
	for(int i=1;i<=wz;i++) now[i]=q[i];
	sort(now+1,now+wz+1,kp);
	build(1,1,m<<2);
	int ll,rr;
	for(int i=1,j;i<=wz;i=j)
	   {ll=now[i].l; rr=now[i].r;
	    j=i+1;
	    while(j<=wz&&now[j].v==now[i].v)//因为每个数只出现一次 
	       {if(ll>now[j].r||now[j].l>rr) return false;
            ll=max(ll,now[j].l);rr=min(rr,now[j].r);
            j++;
		   }
		if(ask(1,ll,rr)) return false;
        for(int k=i;k<j;k++)
           insert(1,now[k].l,now[k].r);
	   }
	return true;
}
void erf()//二分 ,格式吵了标程 
{
	int ll=1,rr=m,mid;
	while(ll<rr)
	   {mid=(ll+rr)>>1;
	    if(check(mid)) ll=mid+1;
	    else rr=mid;
	   }
	if(ll==m&&check(ll)) printf("0\n");
	else printf("%d\n",ll);
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	   {init(); erf();}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值