P2898 [USACO08JAN]haybale猜测Haybale Guessing 二分 + 并查集

二分 + 并查集

题目链接: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:

集合操作注意并查集。

定义全局变量!!

时间: 2024-08-29 19:10:09

P2898 [USACO08JAN]haybale猜测Haybale Guessing 二分 + 并查集的相关文章

POJ 2263 Heavy Cargo(二分+并查集)

题目地址:POJ 2263 这题是在网上的一篇关于优先队列的博文中看到的..但是实在没看出跟优先队列有什么关系..我用的二分+并查集做出来了... 二分路的载重量.然后用并查集检查是否连通. 代码如下: #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <ctype.h> #

HDU 3081Marriage Match II(二分+并查集+网络流之最大流)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3081 有一段时间没写最大流的题了,这题建图居然想了好长时间...刚开始是按着最终的最大流即是做多轮数去想建图,结果根本没思路,后来想了想,可以用二分答案的思想来找最终答案.然后很明显的并查集,但是并查集学的略渣,居然卡在并查集上了..= =. 但是也不是并查集的事..是我建图的思想太正了,稍微用点逆向思维并查集就可以很好利用了. 建图思路是:建立一个源点与汇点,将女孩与源点相连,男孩与汇点相连,权值

hdu 3081 Marriage Match II(最大流 + 二分 + 并查集)

Marriage Match II                                                                           Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description Presumably, you all have known the question of stable

POJ 1797 Heavy Transportation(二分+并查集/kruskal)

Heavy Transportation Time Limit: 3000MS   Memory Limit: 30000K Total Submissions: 24398   Accepted: 6472 Description Background Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand business. But he needs a clever man

bzoj 1196: [HNOI2006]公路修建问题 二分+并查集

题目链接 1196: [HNOI2006]公路修建问题 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1576  Solved: 909[Submit][Status][Discuss] Description OI island是一个非常漂亮的岛屿,自开发以来,到这儿来旅游的人很多.然而,由于该岛屿刚刚开发不久,所以那里的交通情况还是很糟糕.所以,OIER Association组织成立了,旨在建立OI island的交通系统. OI isla

【南开OJ2264】节操大师(贪心+二分+并查集/平衡树)

好久没更新了,今天就随便写一个吧 题目内容 MK和他的小伙伴们(共n人,且保证n为2的正整数幂)想要比试一下谁更有节操,于是他们组织了一场节操淘汰赛.他们的比赛规则简单而暴力:两人的节操正面相撞,碎的一方出局,而没碎的一方晋级(脑补一下端午节的碰鸡蛋游戏>_<).最后经过数轮淘汰决出冠军"节操大师". 通过理性的研究,你测算出他们的节操值分别为1,2,...,n,我们不妨称这个值为"硬度"吧.同时你又测出了一个节操常数k:当两个硬度相差超过k的节操相撞时

[BZOJ1594] [Usaco2008 Jan]猜数游戏(二分 + 并查集)

传送门 题中重要信息,每堆草的数量都不一样. 可以思考一下,什么情况下才会出现矛盾. 1.如果两个区间的最小值一样,但是这两个区间没有交集,那么就出现矛盾. 2.如果两个区间的最小值一样,并且这两个区间有交集,那么这个最小值一定在交集中,但是如果这个交集被某个最小值较大的区间,或是一些最小值较大的区间的并集包含,那么也是矛盾的. 可以二分答案,将这些区间按照最小值从大到小排序,然后可以用线段树维护,也可以用并查集来搞. 下面是用并查集来搞的. 每到一个区间,可以将[l,r]中的f变成r+1,如果

BZOJ 1821 部落划分(二分+并查集)

答案是具有单调性的. 因为最近的两个部落的距离为mid,所以要是有两个野人的距离<mid,则他们一定是一个部落的. 用并查集维护各联通块,如果最后的联通块个数>=k,那么mid还可以再小点.如果<k,mid还可以再大点. 二分搞一搞就行了. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector&

hdu 3081(二分+并查集+最大流||二分图匹配)

Marriage Match II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3558    Accepted Submission(s): 1158 Problem Description Presumably, you all have known the question of stable marriage match. A