Haybale Guessing 猜数游戏
- Description
为了提高智商,锻炼思维能力,奶牛设计了一个猜数游戏。游戏开始前,贝西会在牛棚后面摆上N个数字。所有数字排成一条直线,按次序从1到N编号。每个数字在1到10^9之间,没有两个数字是一样的。
游戏开始后,其他奶牛将会轮流询问贝西Q个问题,每个问题的格式都是一样的:
“位置在Ql到Qr的数字中,最小的数字是多少?”
对每个问题,贝西都会回答一个数字A,不过,她的回答可能是不正确的。请你帮助其他奶牛判断一下,贝西从哪里开始已经出现矛盾了。
- Input Format
第一行:两个用空格分开的整数:N和Q,1 ≤ N ≤ 10^6,1 ≤ Q ≤ 25000
第二行到第Q + 1行:每行三个用空格分开的整数,Ql,Qr和A,表示一个查询,1 ≤ Ql ≤ Qh ≤ N
- Output Format
第一行:如果完全没有矛盾,输出0,否则输出最先造成矛盾的查询编号
- Sample Input
20 4
1 10 7
5 19 7
3 12 8
11 15 12
- Sample Output
3
- Hint
前两个问题的回答可以推出5到10之间最小数量是7,这和第三个回答矛盾
- 分析
首先答案可以二分,因为若前面已经不不合法了,那后面肯定也不合法。
对于二分出来的Lim,我们把所有序号小于等于Lim的边按权值从大到小排序。对于一个区间,如果他已经完全被前面的区间覆盖了,那就说明不合法。对于相同权值的且相交的边,由性质可得我们只需判断这些区间的交是否被覆盖过即可,但是我们应该把这些区间的并全部覆盖。(就这里比较麻烦)
然后就是区间覆盖问题。我们可以用线段树(模板),这里介绍SGG大神的并查集的方法。
对于每一段区间[ll,rr],我们把这段区间都指向ll-1的祖先方向。至于判断一个区间[l,r]是否被覆盖过,只需看看r是否指向了l之前。因为并查集可以压缩路径,所以复杂的降为logn。(这里可能解释的不太清楚,读者可以自行把代码拿下了调试一下)
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,ans,Father[1000006];
struct Data{int s,t,d,id;}a[1000006];
inline bool cmp(const Data&a,const Data&b){
if (a.d!=b.d) return a.d>b.d;
if (a.s!=b.s) return a.s<b.s;
return a.t<b.t;
}
int GetFather(int x){return x==Father[x]?x:Father[x]=GetFather(Father[x]);}
bool Work(int l,int r,int ll,int rr){
if (GetFather(r)<l) return false;
for (int i=rr;i>=ll;i=GetFather(i-1)) Father[i]=i-1;
return true;
}
bool check(int lim){
for (int i=1;i<=n;i++) Father[i]=i;
int l,r,ll,rr,p=0;
for (int i=1,last=0;i<=m+1;i++){
if (a[i].id>lim) continue;
if (!p){
l=ll=a[i].s,r=rr=a[i].t,p=1,last=i;
continue;
}
if (a[i].d==a[last].d && a[i].s<=r) l=a[i].s,ll=min(ll,a[i].s),rr=max(rr,a[i].t);
else{
if (!Work(l,r,ll,rr)) return false;
l=ll=a[i].s; r=rr=a[i].t;
}
last=i;
}
return true;
}
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%d%d%d",&a[i].s,&a[i].t,&a[i].d);
a[i].id=i;
}
sort(a+1,a+1+m,cmp);
for (int l=1,r=m,mid=(l+r)>>1;l<=r;mid=(l+r)>>1){
if (check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
if (ans==m) printf("0");
else printf("%d",ans+1);
fclose(stdin); fclose(stdout);
return 0;
}