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\n", ans);

    return 0;
}

 

posted @ 2017-10-30 21:53  zbtrs  阅读(664)  评论(1编辑  收藏  举报