BZOJ 1594: [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中的数,表示这个问题的答案与它之前的那些回答有冲突之处
注意:如果有冲突出现输出一个数m,使得前M-1个命题不冲突。

 题解: 非常好的一道题.
考虑什么时候会出现问题(矛盾)
1.对于同一个数,出现在了两个不相交的区间.
2.一个区间的最小值已确定,却有一个子区间的最小值小于当前最小值.
我们先二分一个答案 $mid$ ,把 $1$~$mid$ 的所有操作按照权值从大到小排序.
每次处理出每一个权值的区间,如果区间有两个或以上,则不合法.
否则,直接在线段树上查一下该极大区间的区间和.
如果区间和等于区间长度,说明之前已全部被覆盖掉,这是不合法的.
否则,直接覆盖当前区间.
这么迭代下去就行. 

#include<bits/stdc++.h>
#define maxn 3000000
using namespace std;
void setIO(string s)
{
    string in=s+".in"; 
    freopen(in.c_str(),"r",stdin); 
}
int n,Q;   
struct OPT
{
    int l,r,v;    
}opt[maxn],yy[maxn]; 
bool cmp(OPT a,OPT b)
{
    return a.v>b.v; 
}
struct Seg
{
    #define lson (x<<1)
    #define rson ((x<<1)|1)
    int lazy[maxn<<2],sumv[maxn<<2]; 
    void re()
    {
        memset(lazy,0,sizeof(lazy)); 
        memset(sumv,0,sizeof(sumv));    
    }
    void mark(int l,int r,int x)
    {        
        sumv[x]=r-l+1; 
        lazy[x]=1;     
    }
    void pushdown(int l,int r,int x)
    {
        if(lazy[x])
        {
            int mid=(l+r)>>1; 
            if(l<=mid) mark(l,mid,lson); 
            if(r>mid) mark(mid+1,r,rson); 
            lazy[x]=0; 
        }
    }
    void update(int l,int r,int x,int L,int R)
    {
        if(l>=L&&r<=R) 
        {
            mark(l,r,x); 
            return; 
        }
        pushdown(l,r,x); 
        int mid=(l+r)>>1; 
        if(L<=mid) update(l,mid,lson,L,R); 
        if(R>mid) update(mid+1,r,rson,L,R); 
        sumv[x]=sumv[lson]+sumv[rson]; 
    }
    int query(int l,int r,int x,int L,int R)
    {
        if(l>=L&&r<=R) return sumv[x]; 
        pushdown(l,r,x); 
        int mid=(l+r)>>1,tmp=0; 
        if(L<=mid) tmp+=query(l,mid,lson,L,R); 
        if(R>mid)  tmp+=query(mid+1,r,rson,L,R); 
        return tmp;    
    }   
}tr; 
bool check(int mid)
{
    int i,j,k,l1,r1,l2,r2;             
    tr.re(); 
    for(i=1;i<=mid;++i) yy[i]=opt[i]; 
    sort(yy+1,yy+1+mid,cmp); 
    for(i=1;i<=mid;i=j)
    {
        for(j=i;j<=mid&&yy[j].v==yy[i].v;++j); 
        l1=l2=yy[i].l;
        r1=r2=yy[i].r; 
        for(k=i+1;k<j;++k)
        {
            l1=min(l1,yy[k].l); 
            l2=max(l2,yy[k].l); 
            r1=max(r1,yy[k].r); 
            r2=min(r2,yy[k].r); 
        }
        if(l2>r2) return true;     // 无并集
        if(tr.query(1,n,1,l2,r2)==r2-l2+1) return true; 
        tr.update(1,n,1,l1,r1);    
    }
    return false; 
}
int main()
{
    // setIO("input");          
    scanf("%d%d",&n,&Q); 
    for(int i=1;i<=Q;++i)
    {
        scanf("%d%d%d",&opt[i].l,&opt[i].r,&opt[i].v); 
    }    
    int l=1,r=Q,ans=0; 
    while(l<=r)
    {
        int mid=(l+r)>>1; 
        if(check(mid)) 
            r = mid - 1, ans=mid; 
        else 
            l=mid+1; 
    }  
    printf("%d\n",ans); 
    return 0; 
}

  

posted @ 2019-06-17 16:49  EM-LGH  阅读(286)  评论(0编辑  收藏  举报