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 ≤ N; Q 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: Ql, Qh, 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;
}