• YYHS-猜数字(并查集/线段树维护)


    题目描述

        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
    ",ans);
    54     return 0;
    55 }
    View Code
  • 相关阅读:
    树的子结构(剑指offer_26)
    合并两个排序的链表(剑指offer_25)
    反转链表(剑指offer_24)多看多思多想
    链表中环的入口结点(剑指offer_23)
    链表中倒数第k个节点
    调整数组顺序使奇数位于偶数前面(剑指offer_21)
    表示数值的字符串(剑指offer_20)
    1676. 跳石头
    1670. 回合制游戏
    1667. 区间统计(回顾)
  • 原文地址:https://www.cnblogs.com/zhuchenrui/p/7767178.html
Copyright © 2020-2023  润新知