码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 1594: [Usaco2008 Jan]猜数游戏——二分+线段树

时间:2017-09-30 00:33:36      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:i++   max   play   mem   char   推断   sof   get   void   

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个命题不冲突。

————————————————————————————————————

这道题我们可以二分答案

然后判断是否有冲突 冲突有两种

第一种 权值相同的却没有交

第二种 权值不同 但大的包含小的

所以我们二分答案的时候 排序之后判断第一种

第二种涉及到的操作有(线段树)区间覆盖+区间取min 

我们按权值从大到小操作 区间覆盖取并集 询问取交集

就可以了 写了蛮久的QAQ

技术分享
#include<cstdio>
#include<cstring>
#include<algorithm>
using std::sort;
using std::max;
using std::min;
const int M=1e5+7,N=3e6+7;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<0||c>9){if(c==-) f=-1; c=getchar();}
    while(c>=0&&c<=9){ans=ans*10+(c-0); c=getchar();}
    return ans*f;
}
int cnt,n,m,lx[M],rx[M],mn[N],sign[N],ll[M],rr[M];
struct pos{int l,r,v;}q[M],s[M];
bool cmp(pos a,pos b){return a.v>b.v;}
void clear(){
    cnt=1;
    memset(mn,0,sizeof(mn));
    memset(sign,0,sizeof(sign));
}
int L,R;
void up(int x){mn[x]=min(mn[x<<1],mn[x<<1^1]);}
void down(int x){
    if(!sign[x]) return ;
    int ls=x<<1,rs=x<<1^1;
    sign[x]=0; sign[ls]=sign[rs]=1;
    mn[ls]=mn[rs]=1;
}
void modify(int x,int l,int r){
    if(L<=l&&r<=R){
        mn[x]=1; sign[x]=1;
        return ;
    }
    down(x);
    int mid=(l+r)>>1;
    if(L<=mid) modify(x<<1,l,mid);
    if(R>mid) modify(x<<1^1,mid+1,r);
    up(x);
}
int push_mn(int x,int l,int r){
    if(L<=l&&r<=R) return mn[x];
    down(x);
    int mid=(l+r)>>1,ans=1;
    if(L<=mid) ans=min(ans,push_mn(x<<1,l,mid));
    if(R>mid) ans=min(ans,push_mn(x<<1^1,mid+1,r));
    return ans;
}
bool check(int k){
    if(!k) return 1;
    clear(); 
    for(int i=1;i<=k;i++) s[i]=q[i];
    sort(s+1,s+1+k,cmp);
    int color=s[1].v; 
    lx[1]=s[1].l; rx[1]=s[1].r;
    ll[1]=lx[cnt],rr[1]=rx[cnt];
    for(int i=2;i<=k;i++){
        if(s[i].v!=color){
            cnt++; color=s[i].v;
            lx[cnt]=s[i].l; rx[cnt]=s[i].r;
            ll[cnt]=lx[cnt]; rr[cnt]=rx[cnt];
        }
        else lx[cnt]=min(lx[cnt],s[i].l),rx[cnt]=max(rx[cnt],s[i].r),ll[cnt]=max(ll[cnt],s[i].l),rr[cnt]=min(rr[cnt],s[i].r);
        if(ll[cnt]>rr[cnt]) return 1;
    }
    for(int i=1;i<=cnt;i++){
        L=ll[i]; R=rr[i];
        if(push_mn(1,1,n)!=0) return 1;
        L=lx[i]; R=rx[i];
        modify(1,1,n);
    }
    return 0;
}
int main(){
    n=read(); m=read();
    for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].v=read();
    int l=1,r=m+1;
    while(l<r){
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    if(r>m) printf("0\n");
    else printf("%d\n",r);
    return 0;
}
View Code

 

bzoj 1594: [Usaco2008 Jan]猜数游戏——二分+线段树

标签:i++   max   play   mem   char   推断   sof   get   void   

原文地址:http://www.cnblogs.com/lyzuikeai/p/7612966.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!