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; }