• POJ 3657 Haybale Guessing


    POJ_3657

        题目的大意是给出若干个条件x y A,表示区间[x,y]中最小整数为A,而且所有整数都是各不相同的,问到第几个条件的时候就会出现矛盾。

        细想一下后会发现,实际上我们只要能把每个[x,y]的最小值A放好就一定可以构造出一组解,剩下的值可以直接放INF、INF+1之类的东西就可以了。那么我们就可以得到无解的情况,就是对于某个A,发现其不能放在任何一个位置。

        对于任意一个条件x y A,实际上我们可以得到一条信息,就是区间[x,y]内所有整数都大于或等于A。这样满足完所有条件之后,就会得到任意一个数的取值范围。那么接下来就要考虑所有的最小值A是否都可以放到某个位置。这时要考虑一个问题,比如样例中1 10 7和5 19 7,我们就可以得到7只能放在[5,10]中的某个位置,也就是说对于具备相同A的条件,A只能放在这些条件的区间的交集之中。如果这个交集中所有整数都是大于A的话,那么显然A就没有地方可以放了。

        于是我们就得到了一个思路,为了求出在哪个位置出现矛盾,我们先二分Q将其转化成判定性问题,比如现在考虑前n个条件是否会推出矛盾。用线段树维护每个点的最小值,然后遍历一遍条件进行区间修改的操作。最后再将条件排序,求出有相同A的条件的区间的交集,并求交集中的最小值,如果交集为空或者最小值比A大都是不合法的。这样做复杂度是O(logQ*(NlogN+QlogQ+QlogN)),其中logQ*N*logN这个部分太大的,即便使用离散化降到logQ*Q*logQ,整体还是会由于常数过大而TLE。

        于是我们就要想办法优化算法。如果先将n个条件按A降序列,我们就可以将问题变得更简单一些。顺序扫描排序好的条件,每次查询有相同A的条件的区间的交集是否有无色的部分,并对这些区间的并集染色,如果交集为空或者交集中没有无色的部分就是不合法的,其中交集中没有无色的部分就等同于交集中所有的整数都比A大。由于我们只要考虑区间是否被染色,而不用考虑染色的值,这样染色就可以用并查集来实现,降低了复杂度。整体来看,用并查集染色的复杂度是O(N)的,每次查询可以做到O(1)的复杂度,排序的复杂度是O(QlogQ),于是整体的复杂度就是O(logQ*(QlogQ+N+Q))。

        此外我写的递归的并查集会爆栈,于是不得不写成了非递归的形式。

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #define MAXD 1000010
    #define MAXQ 25010
    using namespace std;
    int N, Q, X, p[MAXD], col[MAXD], s[MAXD];
    struct Que
    {
        int x, y, z;    
        bool operator < (const Que &t) const
        {
            return z > t.z;    
        }
    }q[MAXQ], t[MAXQ];
    int find(int x)
    {
        int top = 0;
        while(p[x] != x)
            s[++ top] = x, x = p[x];
        while(top) 
            p[s[top --]] = x;
        return x;
    }
    void init()
    {
        int i;
        for(i = 1; i <= Q; i ++)
            scanf("%d%d%d", &q[i].x, &q[i].y, &q[i].z);
    }
    void refresh(int x, int y)
    {
        int i = find(x - 1), j = find(x);
        if(col[i])
            p[i] = j;
        col[j] = 1;
        for(i = j; i <= y; i = j)
        {
            j = find(i + 1);
            if(col[j] || j <= y)
                col[j] = 1, p[i] = j;
        }
    }
    int deal(int n)
    {
        int i, j, x, y, tx, ty, fa;
        for(i = 0; i <= N + 1; i ++)
            p[i] = i, col[i] = 0;
        for(i = 1; i <= n; i ++)
            t[i] = q[i];
        sort(t + 1, t + 1 + n);
        for(i = 1; i <= n; i = j + 1)
        {
            x = tx = t[i].x, y = ty = t[i].y;
            for(j = i; j < n && t[j + 1].z == t[j].z;)
                ++ j, x = max(x, t[j].x), y = min(y, t[j].y), tx = min(tx, t[j].x), ty = max(ty, t[j].y);
            if(x > y)
                return 0;
            fa = find(x);
            if(col[fa] != 0 && find(fa) >= y)
                return 0;
            refresh(tx, ty);
        }
        return 1;
    }
    void solve()
    {
        int mid, min, max;
        min = 1, max = Q + 1; 
        for(;;)
        {
            mid = (min + max) >> 1;
            if(mid == min)
                break;
            if(deal(mid))
                min = mid;
            else
                max = mid;
        }
        printf("%d\n", mid == Q ? 0 : mid + 1);
    }
    int main()
    {
        while(scanf("%d%d", &N, &Q) == 2)
        {
            init();
            solve();    
        }
        return 0;    
    }
  • 相关阅读:
    数组
    字符对象的方法
    事件
    判断数据类型
    数据类型和变量
    语法
    快速入门
    JavaScript简介
    Spring init-method和destroy-method属性的使用
    spring3后提供了的context:property-placeholder/元素
  • 原文地址:https://www.cnblogs.com/staginner/p/2582539.html
Copyright © 2020-2023  润新知