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;    
}