1. 程式人生 > >YYHS-猜數字(並查集/線段樹維護)

YYHS-猜數字(並查集/線段樹維護)

print urn printf ati view pri lose while 從大到小

題目描述

LYK在玩猜數字遊戲。

總共有n個互不相同的正整數,LYK每次猜一段區間的最小值。形如[li,ri]這段區間的數字的最小值一定等於xi。

我們總能構造出一種方案使得LYK滿意。直到…… LYK自己猜的就是矛盾的!

例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,這顯然就是矛盾的。

你需要告訴LYK,它第幾次猜數字開始就已經矛盾了。

輸入

第一行兩個數n和T,表示有n個數字,LYK猜了T次。
接下來T行,每行三個數分別表示li,ri和xi。

輸出

輸出一個數表示第幾次開始出現矛盾,如果一直沒出現矛盾輸出T+1。

樣例輸入

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

樣例輸出

3

提示

數據範圍
對於50%的數據n<=8,T<=10。 對於80%的數據n<=1000,T<=1000。
對於100%的數據1<=n,T<=1000000,1<=li<=ri<=n,1<=xi<=n(但並不保證一開始的所有數都是1~n的)

Hint
建議使用讀入優化
inline int read()
{
int x = 0, f = 1; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == ‘-‘) f = -1; for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - ‘0‘; return x * f; }

題解

這道題我們先考慮矛盾的情況

我們不難發現有以下兩種情況是矛盾的

1.當一個區間覆蓋了另一個區間且大的區間的x值比另一個區間的x值小的時候是矛盾的

2.當兩個區間的x值相同時,如果這兩個區間沒有交集,這也是矛盾的

知道了矛盾的情況後

我們可以二分矛盾的句子的位置

將前k個句子按x值從大到小排個序,然後我們枚舉,判斷當前區間的x值和前一個區間的x值是否相同

如果相同,就判斷一下有沒有交集

如果不相同,我們可以維護一個線段樹,將交集的區間覆蓋為1,查詢並集的區間是否被覆蓋為1,當然我們也可以用並查集來維護,我是用並查集來做的,但還是感覺線段樹應該好懂一些(雖然代碼長了些)

技術分享
 1 #include<bits/stdc++.h>
 2
#define N 1000005 3 using namespace std; 4 int n,T,cnt; 5 int fa[N]; 6 struct node{ 7 int l,r,x; 8 }a[N],b[N]; 9 bool cmp(node x,node y){ return x.x>y.x; } 10 int find(int x){ if (x!=fa[x]) fa[x]=find(fa[x]); return fa[x]; } 11 bool check(int x){ 12 int f1,f2; 13 for (int i=1;i<=n+1;i++) fa[i]=i; 14 for (int i=1;i<=x;i++) b[i]=a[i]; 15 sort(b+1,b+1+x,cmp); 16 int lmin=b[1].l,lmax=b[1].l,rmin=b[1].r,rmax=b[1].r; 17 for (int i=2;i<=x;i++){ 18 if (b[i].x<b[i-1].x){ 19 f1=find(lmax); 20 if (f1>rmin) return true;//判斷是否有大於b[i].x的區間覆蓋了 21 f2=find(rmax+1); 22 for (int j=find(lmin);j<=rmax;j++){ 23 f1=find(j); 24 fa[f1]=f2; 25 } 26 lmin=lmax=b[i].l; 27 rmin=rmax=b[i].r; 28 } else{ 29 lmin=min(lmin,b[i].l); 30 lmax=max(lmax,b[i].l); 31 rmin=min(rmin,b[i].r); 32 rmax=max(rmax,b[i].r); 33 if (lmax>rmin) return true;//判斷是否有交集 34 } 35 } 36 f1=find(lmax); 37 if (f1>rmin) return true; 38 return false; 39 } 40 int main(){ 41 scanf("%d%d",&n,&T); 42 for (int i=1;i<=T;i++) 43 scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].x); 44 int l=1,r=T; 45 int ans=T+1; 46 while (l<=r){ 47 int mid=(l+r)>>1; 48 if (check(mid)){ 49 ans=mid; 50 r=mid-1; 51 } else l=mid+1; 52 } 53 printf("%d\n",ans); 54 return 0; 55 }
View Code

YYHS-猜數字(並查集/線段樹維護)