「BZOJ 1924」「SDOI 2010」所驼门王的宝藏「Tarjan」

题意

一个\(r\times c\)的棋盘,棋盘上有\(n\)个标记点,每个点有三种类型,类型\(1\)可以传送到本行任意标记点,类型\(2\)可以传送到本列任意标记点,类型\(3\)可以传送到周围八连通任意标记点。求最长路径。

\(r,c\leq 10^6,n\leq 10^5\)

题解

这题做法很多,我就把每一行的所有类型\(1\)门缩到一起(直接找一个代表),列也同理,然后暴力连边,类型\(3\)连边用\(\text{map}\),这样每个点的入边中类型\(1\)\(2\)最多有\(1\)条,类型\(3\)最多\(8\)条,大概可以说明边数和点数同阶,于是\(\text{Tarjan}\)缩点然后\(dp\)求最长路...

#include <algorithm>
#include <utility>
#include <cstdio>
#include <vector>
#include <stack>
#include <map>
using namespace std;

const int N = 1e5 + 10;
const int dx[] = {1, 0, -1, 0, 1, 1, -1, -1};
const int dy[] = {0, 1, 0, -1, -1, 1, -1, 1};

struct node {
    int x, y, z, sz;
} a[N];
int n, r, c, f[N], rt[2][N * 10], dT[N];
vector<int> ob[2][N * 10], G[N], T[N];
map<pair<int, int>, int> ma;
bool isr[N];

int dfn[N], low[N], sz[N], bel[N], scc;
stack<int> st;
bool ins[N];

void tarjan(int u) {
    low[u] = dfn[u] = ++ dfn[0];
    st.push(u); ins[u] = 1;
    for(int i = 0; i < G[u].size(); i ++) {
        int v = G[u][i];
        if(!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if(ins[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u]) {
        scc ++;
        while(1) {
            int v = st.top(); st.pop();
            ins[v] = 0; bel[v] = scc;
            sz[scc] += a[v].sz;
            if(u == v) break ;
        }
    }
}

int pa[N];
int solve(int u) {
    if(pa[u]) return pa[u];
    for(int i = 0; i < T[u].size(); i ++) {
        pa[u] = max(pa[u], solve(T[u][i]));
    }
    pa[u] += sz[u];
    return pa[u];
}

int main() {
    scanf("%d%d%d", &n, &r, &c);
    for(int i = 1; i <= n; i ++) {
        scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
        ma[make_pair(a[i].x, a[i].y)] = i;
        ob[0][a[i].x].push_back(i);
        ob[1][a[i].y].push_back(i);
        a[i].sz = 0;
    }
    for(int i = 1; i <= n; i ++) {
        if(a[i].z == 1) {
            int &u = rt[0][a[i].x];
            if(!u) u = i;
            a[u].sz ++; f[i] = u;
        }
        if(a[i].z == 2) {
            int &u = rt[1][a[i].y];
            if(!u) u = i;
            a[u].sz ++; f[i] = u;
        }
        if(a[i].z == 3) {
            f[i] = i;
            a[i].sz ++;
        }
        isr[f[i]] = 1;
    }
    for(int i = 1; i <= n; i ++)
        if(isr[i]) {
            if(a[i].z == 1) {
                for(int j = 0; j < ob[0][a[i].x].size(); j ++) {
                    int v = ob[0][a[i].x][j];
                    G[i].push_back(f[v]);
                }
            }
            if(a[i].z == 2) {
                for(int j = 0; j < ob[1][a[i].y].size(); j ++) {
                    int v = ob[1][a[i].y][j];
                    G[i].push_back(f[v]);
                }
            }
            if(a[i].z == 3) {
                for(int j = 0; j < 8; j ++) {
                    int v = ma[make_pair(a[i].x + dx[j], a[i].y + dy[j])];
                    if(v) {
                        G[i].push_back(f[v]);
                    }
                }
            }
        }
    for(int i = 1; i <= n; i ++)
        if(isr[i] && !dfn[i]) {
            tarjan(i);
        }
    for(int i = 1; i <= n; i ++) {
        if(isr[i]) {
            for(int j = 0; j < G[i].size(); j ++) {
                int v = G[i][j];
                if(bel[i] != bel[v]) {
                    T[bel[i]].push_back(bel[v]);
                    dT[bel[v]] ++;
                }
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= scc; i ++)
        if(!dT[i]) {
            ans = max(ans, solve(i));
        }
    printf("%d\n", ans);
    return 0;
}

posted @ 2019-03-30 11:51  hfhongzy  阅读(168)  评论(0编辑  收藏  举报