zoukankan      html  css  js  c++  java
  • noip模拟赛 猜数字

    题目描述
    LYK在玩猜数字游戏。
    总共有n个互不相同的正整数,LYK每次猜一段区间的最小值。形如[li,ri]这段区间的数字的最小值一定等于xi。
    我们总能构造出一种方案使得LYK满意。直到…… LYK自己猜的就是矛盾的!
    例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,这显然就是矛盾的。
    你需要告诉LYK,它第几次猜数字开始就已经矛盾了。

    输入格式(number.in)
    第一行两个数n和T,表示有n个数字,LYK猜了T次。
    接下来T行,每行三个数分别表示li,ri和xi。

    输出格式(number.out)
    输出一个数表示第几次开始出现矛盾,如果一直没出现矛盾输出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;
    }

    分析:有点难度的一道题.

          总是感觉题意自相矛盾,打的50分暴力只得了40分?最后看数据n=8, 1 8 2这种猜测竟然合法!题目中明明说了每个数字都是正整数并且互不相同,虽然最后说了并不保证一开始的所有数都是1~n的,但我并不明白这前后是啥意思.

          40分暴力根据题目意思来就好了,相当于是一个1~n的全排列,每次判断这个排列到几个猜测就不合法了,记录下最大的就是答案.生成全排列可以用STL中的next_permutation.

          100分的做法比较神,由于每个区间只能知道它的最小值,并不知道它在哪,其它的值是什么.现在要确定的就是怎么样才能判断一次猜测是不合法的.从题目给的样例可以看出,如果一个x较小的区间被x较大的区间给完全覆盖住了,那么这就是不合法的.根据这种判断方法,可以先对所有区间按照x从大到小排序,看这个区间有没有被之前的区间给覆盖.这就有一个问题:我不知道这个区间是第几次询问,那么二分第k次出现询问,把第一个到第k个区间排序就行了.

          进行覆盖操作肯定不能一个一个暴力覆盖,可以用线段树来维护,但是会T掉两个点,正解是并查集,维护每个点能延伸到最右边的哪个点,判断是否大于当前区间的右端点.对于许多个x值相等的区间,可以证明的是最小值一定在它们的交集中,因为每个数都是正整数并且互不相等,所以只需要关注是否交集被完全覆盖了即可.如果x值一定的区间只有一个就判断这个区间是否被完整覆盖即可.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int n, T, ans, fa[1000010];
    
    struct node
    {
        int l, r, x;
    }p[1000010],e[1000010];
    
    bool cmp(node a, node b)
    {
        return a.x > b.x;
    }
    
    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++)
            p[i] = e[i];
        sort(p + 1, p + 1 + k,cmp);
        int lmax = p[1].l, lmin = p[1].l, rmax = p[1].r, rmin = p[1].r;
        for (int i = 2; i <= k; i++)
        {
            if (p[i].x < p[i - 1].x)
            {
                if (find(lmax) > rmin)
                    return true;
                for (int j = find(lmin); j <= rmax; j++)
                    fa[find(j)] = find(rmax + 1);
                lmin = lmax = p[i].l;
                rmin = rmax = p[i].r;
            }
            else
            {
                lmin = min(lmin, p[i].l);
                lmax = max(lmax, p[i].l);
                rmin = min(rmin, p[i].r);
                rmax = max(rmax, p[i].r);
                if (find(lmax) > rmin)
                    return true;
            }
        }
        if (find(lmax) > rmin)
            return true;
        return false;
    }
    
    int main()
    {
        scanf("%d%d", &n, &T);
        for (int i = 1; i <= T; i++)
            scanf("%d%d%d", &e[i].l, &e[i].r, &e[i].x);
        int l = 1, r = T;
        ans = T + 1;
        while (l <= r)
        {
            int mid = (l + r) >> 1;
            if (check(mid))
            {
                ans = mid;
                r = mid - 1;
            }
            else
                l = mid + 1;
        }
        printf("%d
    ", ans);
    
        return 0;
    }
  • 相关阅读:
    几个ID
    一百层高楼和两个棋子
    快速了解的链接 shell sed awk
    用shell实现一个“输入密码”程序
    i love you do you love me
    打造全新视觉环境
    【转】LINUX 环境变量总结
    TextBox输入限制
    获取CPU和硬盘序列号
    Lable属性设置(winform)
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7756840.html
Copyright © 2011-2022 走看看