【bzoj1594】猜數遊戲
1594: [Usaco2008 Jan]猜數遊戲
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 556 Solved: 225
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 41 10 7
5 19 7
3 12 8
11 15 12
輸入說明:
編號為1..10的草堆中,最小的那堆裏有7捆草,編號為5..19的草堆中同樣
捆。
Sample Output
3輸出說明:
對於第3個問題“3 12”的回答“8”與前面兩個回答沖突。因為每堆中草的
捆數唯一,從前兩個回答中我們能推斷出,編號為5..10的幹草堆中最小的那堆
裏有7捆幹草。很顯然,第3個問題的回答與這個推斷沖突。
HINT
註意:如果有沖突出現輸出一個數m,使得前M-1個命題不沖突。
試題分析:首先要確定在什麽情況下會出現矛盾:
1.當存在一個(x1,y1,r)與另一個三元組(x,y,r)兩個區間不相交時不存在解
2.當存在一個(x1,y1,r1)與另一個三元組(x,y,r)中,r1>r,[x1,y1]包括[x,y],那麽顯然此情況不合法。
第一個判斷只需要順次判斷記錄一個交集一個並集即可,第二個判斷線段樹維護。
此時會發現一個問題,順次修改並不能判斷1在哪裏出現矛盾或判斷在2出現矛盾。
這樣我們就需要考慮二分一個答案,然後檢驗這個答案以及之前是否會出現矛盾。
先離散化一下r,然後對於每個r求出其詢問的交集和並集。
然後我們就可以知道判斷1只需要使用交集判斷,如果一個r的詢問和之前r詢問的交集沒有相交,那麽就矛盾。
對於判斷2,從Q~1枚舉r,我們的線段樹只需要維護一下兩個操作:
1.查詢區間與運算
2.將一段區間置為1
這是很好維護的,然後註意一點,取區間與需要判斷的是交集,而詢問中(x,y,r)相當於聲明[x,y]這段區間的最小值已經是r了,那麽就需要在線段樹中將[x並,y並](對於詢問答案r的)這一段區間置為1,而不是取交集置為1。
代碼:
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<algorithm> using namespace std; inline int read(){ int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f; } const int MAXN = 1000001; const int INF = 999999; int N,Q; struct data{ int x,y,r; int id; }a[MAXN+1]; int ans; int P[MAXN+1],cnt; bool cmp(data a,data b){ return a.r<b.r; } struct row{ int x,y; int x1,y1; }t[MAXN+1]; int tr[MAXN*4+1]; int col[MAXN*4+1]; int mint(int a,int b){ return min((a==INF?-INF:a),(b==INF?-INF:b)); } void Lazy_tage(int l,int r,int rt){ if(!col[rt]) return ; tr[rt*2]=1; tr[rt*2+1]=1; tr[rt]=1; col[rt*2]=1; col[rt*2+1]=1; col[rt]=0; } int Min(int rt,int l,int r,int L,int R){ if(L<=l&&R>=r){ return tr[rt]; } Lazy_tage(l,r,rt); int mid=(l+r)>>1,ans=1; if(mid>=L) ans=ans&Min(rt*2,l,mid,L,R); if(mid<R) ans=ans&Min(rt*2+1,mid+1,r,L,R); return ans; } void Add(int rt,int l,int r,int L,int R){ if(L<=l&&R>=r){ tr[rt]=1; col[rt]=1; return ; } Lazy_tage(l,r,rt); int mid=(l+r)>>1; if(mid>=L) Add(rt*2,l,mid,L,R); if(mid<R) Add(rt*2+1,mid+1,r,L,R); tr[rt]=tr[rt*2]&tr[rt*2+1]; return ; } bool check(int k){ memset(tr,0,sizeof(tr)); memset(col,0,sizeof(col)); for(int i=1;i<=Q;i++) t[i].x=t[i].y=-1; for(int i=1;i<=Q;i++){ if(a[i].id>k) continue; if(t[a[i].r].x==-1){ t[a[i].r].x=a[i].x;//x,y為並集 t[a[i].r].y=a[i].y; t[a[i].r].x1=a[i].x;//x1,y1為交集 t[a[i].r].y1=a[i].y; } else{ if(a[i].x>t[a[i].r].y||a[i].y<t[a[i].r].x) return false; t[a[i].r].x=max(t[a[i].r].x,a[i].x); t[a[i].r].y=min(t[a[i].r].y,a[i].y); t[a[i].r].x1=min(t[a[i].r].x1,a[i].x); t[a[i].r].y1=max(t[a[i].r].y1,a[i].y); } } for(int i=Q;i>=1;i--){ if(t[i].x!=-1){ int op=Min(1,1,N,t[i].x,t[i].y);//查詢r的並集,只有並集中才可能出現r if(op==1) return false; Add(1,1,N,t[i].x1,t[i].y1);//這裏需要將交集加上 } } return true; } int main(){ N=read(),Q=read(); for(int i=1;i<=Q;i++){ a[i].x=read(); a[i].y=read(); a[i].r=read(); P[++cnt]=a[i].r; a[i].id=i; } sort(P+1,P+cnt+1); for(int i=1;i<=Q;i++) a[i].r=lower_bound(P+1,P+cnt+1,a[i].r)-P;//離散化r sort(a+1,a+Q+1,cmp); int l=1,r=Q; while(l<=r){ int mid=(l+r)>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",(ans+1)%(Q+1)); }
【bzoj1594】猜數遊戲