【BZOJ1594】[Usaco2008 Jan]猜数游戏 二分答案+并查集

【BZOJ1594】[Usaco2008 Jan]猜数游戏

Description

为了提高自己低得可怜的智商,奶牛们设计了一个新的猜数游戏,来锻炼她们的逻辑推理能力。 游戏开始前,一头指定的奶牛会在牛棚后面摆N(1 <= N<= 1,000,000)堆干草,每堆有若干捆,并且没有哪两堆中的草一样多。所有草堆排成一条直线,从左到右依次按1..N编号,每堆中草的捆数在1..1,000,000,000之间。 然后,游戏开始。另一头参与游戏的奶牛会问那头摆干草的奶牛 Q(1 <= Q <= 25,000)个问题,问题的格式如下: 编号为Ql..Qh(1 <= Ql <= Qh <= N)的草堆中,最小的那堆里有多少捆草? 对于每个问题,摆干草的奶牛回答一个数字A,但或许是不想让提问的奶牛那么容易地得到答案,又或许是她自己可能记错每堆中干草的捆数,总之,她的回答不保证是正确的。 请你帮助提问的奶牛判断一下,摆干草的奶牛的回答是否有自相矛盾之处。

Input

* 第1行: 2个用空格隔开的整数:N 和 Q

* 第2..Q+1行: 每行为3个用空格隔开的整数Ql、Qh、A,描述了一个问题以及它 对应的回答

Output

* 第1行: 如果摆干草的奶牛有可能完全正确地回答了这些问题(也就是说,能 找到一种使得所有回答都合理的摆放干草的方法),输出0,否则输出 1个1..Q中的数,表示这个问题的答案与它之前的那些回答有冲突之处

Sample Input

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

输入说明:

编号为1..10的草堆中,最小的那堆里有7捆草,编号为5..19的草堆中同样
如此;编号为3..12的草堆中最小的堆里是8捆草,11..15堆中的最小的堆里是12
捆。

Sample Output

3

输出说明:

对于第3个问题“3 12”的回答“8”与前面两个回答冲突。因为每堆中草的
捆数唯一,从前两个回答中我们能推断出,编号为5..10的干草堆中最小的那堆
里有7捆干草。很显然,第3个问题的回答与这个推断冲突。

HINT

注意:如果有冲突出现输出一个数m,使得前M-1个命题不冲突。

题解:先二分答案,然后将前mid个询问按照A从大到小排序,然后判断:如果几个A比较大的区间的并集包含了一些A比较小的区间的交集,那么一定出现矛盾

区间的包含关系我们可以用一个线段树来维护,当然,另一种神奇的做法就是用并查集

如果我们要覆盖[l,r]这段区间,那么我们就暴力令[l,r]中所有元素的fa都指向r+1,(可以证明均摊的复杂度很小),然后判断某个区间[ll,rr]是否被覆盖过是就看find(ll)是否大于rr就行了

注意所有干草堆的大小都不相同

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int n,m;
int f[1000010];
struct QUERY
{
    int qa,qb,qv;
}q[25010],p[25010];
int find(int x)
{
    return (f[x]==x)?x:(f[x]=find(f[x]));
}
bool cmp(QUERY a,QUERY b)
{
    return a.qv>b.qv;
}
bool solve(int sta)
{
    int i,j,l1,l2,r1,r2;
    for(i=1;i<=n+1;i++)    f[i]=i;
    for(i=1;i<=sta;i++)    q[i].qa=p[i].qa,q[i].qb=p[i].qb,q[i].qv=p[i].qv;
    sort(q+1,q+sta+1,cmp);
    l1=l2=q[1].qa,r1=r2=q[1].qb;
    for(i=2;i<=sta;i++)
    {
        if(q[i].qv<q[i-1].qv)
        {
            if(find(l1)>r1)    return false;
            for(j=f[l2];j<=r2;j=f[j])
            {
                find(j);
                f[j]=find(j+1);
            }
            l1=l2=q[i].qa,r1=r2=q[i].qb;
        }
        else
        {
            l1=max(l1,q[i].qa),l2=min(l2,q[i].qa),r1=min(r1,q[i].qb),r2=max(r2,q[i].qb);
            if(r1<l1)    return false;
        }
    }
    if(find(l1)>r1)    return false;
    return true;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)    scanf("%d%d%d",&p[i].qa,&p[i].qb,&p[i].qv);
    int l=1,r=m+1,mid;
    while(l<r)
    {
        mid=l+r>>1;
        if(solve(mid))    l=mid+1;
        else    r=mid;
    }
    printf("%d",l>n?0:l);
    return 0;
}
posted @ 2017-02-16 14:59  CQzhangyu  阅读(738)  评论(5编辑  收藏  举报