题解 P2403 【[SDOI2010]所驼门王的宝藏】

题解:Tarjan + 拓扑排序 + 建图小技巧

观察题目性质,大概就是要Tarjan缩点后通过拓扑排序求出一个一条最长路径(经过的点最多)
难度就在建图这里.

我着重分享一个我的建图技巧:

首先将给出的门按横坐标排序,先处理横门。 如果当前有一个横门,那么我们将它与同一横行的所有边连边,记录下这个门的编号,接下来,同一行的横门只需要向它连边就行了。这样子不会影响答案而且大大减少了无用边。

同样的道理,接着重新把给出的门按纵坐标排序,处理纵门。如果当前是一个纵门,我们就把它向所有同一纵行的门连边(因为是排过序的,所以比较好处理)。然后同一行的纵门向它连边就行了。

对于任意门的处理我们使用STL中的map,记录下每个门的坐标,将Map中每个门对应的值都设为这个门的编号,然后循环从1到n每次暴力的看八个方向有没有门就行了,有就连上。

这样子就建好图了,然后跑一遍tarjan和拓扑排序就好了。

对于map简单介绍一下:

可以看成是一个数组,但是这个数组的下标比较奇怪,它的下标可以是一个"pair" 类型。类似于可以把一个二维数组压缩为一个一维数组,拿上面记录坐标来看,我们定义一个

pair <int,int> p

p.first = x

p.second = y;

那么我们的

map <pair<int,int> , int> mp,mp[p] <-----这里mp的下标是一个pair

就可以直接给mp[p]赋值.

前面的pair可以有其他很多种类型,根据题目需要可以进行修改

如果用普通的数组我们显然得写为mp[x][y],这样子的话我们就得开一个二维数组了.

map的本质是一棵红黑树,它的时间复杂度是o(NlogN)的,空间复杂度是O(元素个数)的,所以在数据范围比较小的时候用一个二维数组会在时间复杂度上更优,但是这道题中R以及C都是 <= 1e6的,我们显然开不下一个mp[R][C] ,所以,这时候应该用map

tarjan以及拓扑排序就不着重讲了(应该做这道题不应该不会tarjan了吧。不会吧,不会吧,会吧,吧...如果不会的话康康代码吧)吐槽一下,这个题的代码是真的长啊!恶心!恶心!

Code

#include <bits/stdc++.h>
using namespace std;
pair<int, int> p;
map<pair<int, int>, int> mp;
int n, m = 0, R, C;
struct ask {
    int x, y, z, num;
} q[100005];
struct node {
    int u, v;
} r[10000005], T[10000005];
int start[100005], Fx[100005], Fy[100005], dfn[100005], low[100005], color[100005];
int cmpa(ask A, ask B) { return A.x < B.x; }
int cmpb(ask A, ask B) { return A.y < B.y; }
void add(int u, int v) {
    if (u == v)
        return;
    m++;
    r[m].u = u, r[m].v = v;
}
void build_graph() {
    sort(q + 1, q + 1 + n, cmpa);
    int ls = -1;
    for (int i = 1; i <= n; i++)
        if (ls != q[i].x)
            ls = q[i].x, start[ls] = i;
    for (int i = 1; i <= n; i++) {
        if (q[i].z == 1 && Fx[q[i].x] == 0) {
            for (int j = start[q[i].x]; j <= n && q[j].x == q[i].x; j++)
                if (i != j)
                    add(q[i].num, q[j].num);
            Fx[q[i].x] = q[i].num;
        }
        if (q[i].z == 1 && Fx[q[i].x] != 0)
            add(q[i].num, Fx[q[i].x]);
    }
    sort(q + 1, q + 1 + n, cmpb);
    memset(start, 0, sizeof(start));
    ls = -1;
    for (int i = 1; i <= n; i++)
        if (ls != q[i].y)
            ls = q[i].y, start[ls] = i;
    for (int i = 1; i <= n; i++) {
        if (q[i].z == 2 && Fy[q[i].y] == 0) {
            for (int j = start[q[i].y]; j <= n && q[j].y == q[i].y; j++)
                if (i != j)
                    add(q[i].num, q[j].num);
            Fy[q[i].y] = q[i].num;
        }
        if (q[i].z == 2 && Fy[q[i].y] != 0)
            add(q[i].num, Fy[q[i].y]);
    }
    memset(start, 0, sizeof(start));
}
int d1[9] = { 0, 1, -1, 0, 0, 1, -1, 1, -1 };
int d2[9] = { 0, 0, 0, 1, -1, -1, 1, 1, -1 };
int now = 0, tot = 0;
int tack[100005], tail = 0, z = 0, Siz[100005], LCNB[100005];
int tarjan(int x) {
    now++;
    low[x] = dfn[x] = now;
    tail++, tack[tail] = x;
    LCNB[x] = 1;
    for (int i = start[x]; i <= m && r[i].u == x; i++) {
        int to = r[i].v;
        if (!dfn[to]) {
            tarjan(to);
            low[x] = min(low[x], low[to]);
        } else if (LCNB[to])
            low[x] = min(low[x], dfn[to]);
    }
    if (low[x] == dfn[x]) {
        z++;
        while (tack[tail + 1] != x && tail >= 1)
            color[tack[tail]] = z, LCNB[tack[tail]] = 0, Siz[z]++, tail--;
    }
    return 0;
}
int cmp(node A, node B) { return A.u < B.u; }
int mm = 0, locked[1000005], sum[100005] = { 0 };
void newadd(int u, int v) {
    if (u == v)
        return;
    mm++;
    T[mm].u = u, T[mm].v = v;
}
int topsort() {
    queue<int> q;
    for (int i = 1; i <= n; i++)
        if (locked[i] == 0 && Siz[i] != 0)
            q.push(i);
    for (int i = 1; i <= n; i++) sum[i] = Siz[i];
    while (!q.empty()) {
        int k = q.front();
        q.pop();
        for (int i = start[k]; i <= m && r[i].u == k; i++) {
            int v = r[i].v;
            sum[v] = max(sum[v], sum[k] + Siz[v]);
            locked[v]--;
            if (locked[v] == 0)
                q.push(v);
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) ans = max(ans, sum[i]);
    return ans;
}
int main() {
    cin >> n >> R >> C;
    for (int i = 1; i <= n; i++) {
        cin >> q[i].x >> q[i].y >> q[i].z;
        p.first = q[i].x;
        q[i].num = i;
        p.second = q[i].y;
        mp[p] = q[i].num;
    }
    build_graph();
    for (int i = 1; i <= n; i++) {
        if (q[i].z == 3) {
            for (int k = 1; k <= 8; k++) {
                p.first = q[i].x + d1[k];
                p.second = q[i].y + d2[k];
                if (mp[p] != 0)
                    add(q[i].num, mp[p]);
            }
        }
    }
    sort(r + 1, r + 1 + m, cmp);
    int ls = -1;
    for (int i = 1; i <= m; i++)
        if (ls != r[i].u)
            ls = r[i].u, start[ls] = i;
    for (int i = 1; i <= n; i++)
        if (!dfn[i])
            tarjan(i);
    mp.clear();
    for (int i = 1; i <= m; i++) {
        p.first = color[r[i].u];
        p.second = color[r[i].v];
        if (mp[p] == 0)
            newadd(color[r[i].u], color[r[i].v]);
        mp[p] = 1;
    }
    m = mm;
    memset(start, 0, sizeof(start));
    for (int i = 1; i <= m; i++) r[i] = T[i];
    sort(r + 1, r + 1 + m, cmp);
    ls = -1;
    for (int i = 1; i <= m; i++)
        if (ls != r[i].u)
            ls = r[i].u, start[ls] = i;
    for (int i = 1; i <= m; i++) locked[r[i].v]++;
    cout << topsort();
    return 0;
}

完结撒花!O(∩_∩)O~~ , (;′⌒`)点个赞呗。(代码写这么丑还要赞的真是臭不要脸 合情合理

posted @ 2020-10-24 16:33  MYCui  阅读(130)  评论(0编辑  收藏  举报