1. 程式人生 > >P2898 [USACO08JAN]haybale猜測Haybale Guessing 二分 + 並查集

P2898 [USACO08JAN]haybale猜測Haybale Guessing 二分 + 並查集

兩個 width con 才會 mas include tchar 什麽 是不是

二分 + 並查集

題目鏈接:https://www.luogu.org/problemnew/show/2898

題目大意:

給一段長度為n,每個位置上的數都不同的序列a[1..n]和q和問答,

每個問答是(x, y, r)代表RMQ(a, x, y) = r,

要你給出最早的有矛盾的那個問答的編號。

首先,要你求的是出現矛盾的,那什麽時候才會出現矛盾呢?

可以總結為兩種情況:

之前已更新這個區間最小值為x,又要更新此區間的子區間(或這個區間)的最小值為更小的數

這樣~

技術分享

(黑色是已更新區間,紅色是這次操作的區間。)

②兩段區間的最小值相同,但沒有交集

(因為題目中說數字互不相同,所以不可能一個數字同時出現在兩個位置~QAQ)

就是這樣子~

技術分享

但是註意合並的時候要合並到r + 1,為了防止這種情況:

③已知[1,2] Min = 2 , [3,4] Min = 4,現在要合並一段區間[1,4] Min = 1,

很明顯是矛盾的,但是如果把fa合並到r不會判出矛盾。

技術分享

怎麽判是不是有交集和是不是完全被包含呢?

有沒有發現我在反復用一個詞:集合!!

集合操作什麽的當然是並查集!

那怎麽找到矛盾的操作呢?

二分!(10^6)

為了容易判斷情況二,先讓Min從大到小排個序

判斷情況一:

if(t[i].Min < t[i - 1].Min){
     if(find(lmax) > rmin) //滿足情況一
         
return true; for(int j = lmin;j <= rmax;++ j) //合並上一個區間
     fa[find[j] = find(rmax + 1);//都合並到rmax + 1,為了防止情況③
lmax = lmin = t[i].l;//更新成這個區間的l,r rmax = rmin = t[i].r; }

其實合並的時候,每次都找一遍find(j)相當於把已經合並過的集合又每個點都掃了一遍,

但是已經合並過其實可以直接跳到find(j),

就是這樣:

for(int j = lmin;j <= rmax;++ j){
                j 
= find(j); fa[j] = find(rmax + 1); }

這裏是1s和2s的區別

判斷情況二:

else if(t[i].Min == t[i - 1].Min){
            lmax = max(lmax,t[i].l);
            lmin = min(lmin,t[i].l);
            rmax = max(rmax,t[i].r);
            rmin = min(rmin,t[i].r);
            if(lmax > rmin)//看圖理解
                return true;
}

整個Check就是這樣的:

bool check(int k){//前k句話是否出現過矛盾
    for(int i = 1;i <= n + 1;++ i) fa[i] = i;
    for(int i = 1;i <= k;++ i) t[i] = a[i];
    int lmin,lmax,rmin,rmax;
    sort(t + 1,t + 1 + k,cmp);//為了好判斷情況二,先把 Min sort一遍
    lmax = lmin = t[1].l;
    rmin = rmax = t[1].r;
    for(int i = 2;i <= k;++ i){
        if(t[i].Min < t[i - 1].Min){//情況二
            if(find(lmax) > rmin)
                return true;
            for(int j = lmin;j <= rmax;++ j){
                j = find(j);
                fa[j] = find(rmax + 1);
                //fa[find(j)] = find(rmax + 1);
            }
            lmax = lmin = t[i].l;
            rmax = rmin = t[i].r;
        }
        else {//情況一
            lmax = max(lmax,t[i].l);
            lmin = min(lmin,t[i].l);
            rmax = max(rmax,t[i].r);
            rmin = min(rmin,t[i].r);
            if(lmax > rmin)
                return true;
        }
    }
    if(find(lmax) > rmin)
        return true;
    return false;
}

for到k就可以了,因為在出來while的時候又判了一遍。

還有註意定義全局變量QAQQQQQ

這個題就是這樣了~

Codes:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
const int N = 1000000 + 10;
int n,ans,q;
int fa[N];
struct node{
    int l,r,Min;
}t[N],a[N];

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;
}
bool cmp(node x,node y){
    return x.Min > y.Min;
}
int find(int x){
    if(x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}
bool check(int k){
    for(int i = 1;i <= n + 1;++ i) fa[i] = i;
    for(int i = 1;i <= k;++ i) t[i] = a[i];
    int lmin,lmax,rmin,rmax;
    sort(t + 1,t + 1 + k,cmp);
    lmax = lmin = t[1].l;
    rmin = rmax = t[1].r;
    for(int i = 2;i <= k;++ i){
        if(t[i].Min < t[i - 1].Min){
            if(find(lmax) > rmin)
                return true;
            for(int j = lmin;j <= rmax;++ j){
                j = find(j);
                fa[j] = find(rmax + 1);
                //fa[find(j)] = find(rmax + 1);
            }
            lmax = lmin = t[i].l;
            rmax = rmin = t[i].r;
        }
        else if(t[i].Min == t[i - 1].Min){
            lmax = max(lmax,t[i].l);
            lmin = min(lmin,t[i].l);
            rmax = max(rmax,t[i].r);
            rmin = min(rmin,t[i].r);
            if(lmax > rmin)
                return true;
        }
    }
    if(find(lmax) > rmin)
        return true;
    return false;
}
int main(){
    n = read();
    q = read();
    for(int i = 1;i <= q;++ i)
        a[i].l = read(),
        a[i].r = read(),
        a[i].Min = read();
    int l = 1,r = q ;
    ans = 0;
    while(r - l > 1){
        int mid = (l + r) >> 1;
        if(check(mid))
            ans = mid,
            r = mid;
        else 
            l = mid;
    }
    cout << ans << \n;
    return 0;
}

MAS:

集合操作註意並查集

定義全局變量!!

P2898 [USACO08JAN]haybale猜測Haybale Guessing 二分 + 並查集