【USACO】2008 Jan Haybale Guessing 猜数游戏

1 篇文章 0 订阅
1 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值