图论/主席树优化建图-小火车什么什么三维宇宙

题目大意:给 (n (nle 100,000)) 个三元组,保证每个数每一维构成 ([1,n]) 的排列,定义大于表示一个数至少两维比另一个数大,求某个顺序比较之后可能是最大的数的数具体是哪些。

大于不具有传递性。

入度为 (0) 的点一定可以是最大值,只要从这个点开始,每次随便找一个其他点比较就行。

如果有环,考虑最终让 (a_i) 作为最大值,只要从大于 (a_i)(a_k) 开始,与大于 (a_k)(a_t) 比较,得到当前最大值 (a_t) ,继续……最后再与 (a_i) 比较即可。

所以环上任意点都可以是最大值。

两个数不是 (a>b) 就是 (a<b) ,即两点间一定有边,所以缩点之后入度为 (0) 的强连通分量只有一个。

枚举“至少两维”中是哪两维,然后连边。如果升序讨论 (x_i) ,则应连向 (y_t < x_i, x_t < x_i) 的数。第一个限制,对 (y) 开权值线段树。第二个限制,把线段树开成可持久化的,每次添加完边后,再新增一条从根节点到 (y_i) 号叶子节点,并从叶子连向对应数的路径。

Tarjan 写错变量名调了半天 Orz 。

#include <cstdio>
#include <ctype.h>
#include <cmath>
#include <algorithm>

using namespace std;

char *p1, *p2, buf[1 << 20];

inline char gc()
{
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++;
}

template<typename T>
void rd(T &num)
{
    char tt;
    bool flag = 0;
    while (!isdigit(tt = gc()) && tt != '-');
    if (tt == '-')
        num = 0, flag = 1;
    else
        num = tt - '0';
    while (isdigit(tt = gc()))
        num = num * 10 + tt - '0';
    if (flag)
        num = -num;
    return;
}

const int _N = 101000;
const int _M = 13500000;

typedef int lar[_M];

int EC, FK, N, ST_cnt, Stamp, S_cnt, SCC;
int B[_N], X[_N], Y[_N], Z[_N], Rt[_N];
lar Nxt, Fst, V, Fk_nxt, Fk_fst, Fk_v, L, R, Low, Dfn, S, Bel, Ind, Q;
bool Mk[_M];

void ins_edge(int a, int b)
{
	if (!a || !b)
		return;
    ++EC, Nxt[EC] = Fst[a], Fst[a] = EC, V[EC] = b;
    return;
}

void fuck_edge(int a, int b)
{
    ++FK, Fk_nxt[FK] = Fk_fst[a], Fk_fst[a] = FK, Fk_v[FK] = b;
    return;
}

void ins_path(int &p, int &q, int l, int r, int num, int val)
{
    p = ++ST_cnt;
    if (l == r) {
        ins_edge(p, num);
        return;
    }
    int mid = (l + r) >> 1;
    if (val <= mid)
        R[p] = R[q], ins_path(L[p], L[q], l, mid, num, val);
    else
        L[p] = L[q], ins_path(R[p], R[q], mid + 1, r, num, val);
    ins_edge(p, L[p]), ins_edge(p, R[p]);
    return;
}

void connect(int &p, int l, int r, int num, int s, int t)
{
    if (s > t || !p)
        return;
    if (s <= l && r <= t) {
        ins_edge(num, p);
        return;
    }
    int mid = (l + r) >> 1;
    if (s <= mid)
        connect(L[p], l, mid, num, s, t);
    if (t > mid)
        connect(R[p], mid + 1, r, num, s, t);
    return;
}

void owo(int *x, int *y)
{
    for (int i = 1; i <= N; ++i)
        B[x[i]] = i;
    Rt[0] = 0;
    for (int i = 1; i <= N; ++i) {
        connect(Rt[i - 1], 1, N, B[i], 1, y[B[i]] - 1);
        ins_path(Rt[i], Rt[i - 1], 1, N, B[i], y[B[i]]);
    }
    return;
}

void dfs(int p)
{
    Low[p] = Dfn[p] = ++Stamp, Mk[p] = 1, S[++S_cnt] = p;
    for (int i = Fst[p]; i; i = Nxt[i]) {
        int &t = V[i];
        if (!Dfn[t])
            dfs(t), Low[p] = min(Low[p], Low[t]);
        else if (Mk[t])
            Low[p] = min(Low[p], Dfn[t]);
    }
    if (Low[p] != Dfn[p])
        return;
    int tmp;
    ++SCC;
    do {
        tmp = S[S_cnt--], Mk[tmp] = 0, Bel[tmp] = SCC;
    } while (tmp != p);
    return;
}

int main()
{
    rd(N);
    for (int i = 1; i <= N; ++i)
        rd(X[i]), rd(Y[i]), rd(Z[i]);
    ST_cnt = N;
    owo(X, Y), owo(X, Z), owo(Y, Z);
    for (int i = 1; i <= ST_cnt; ++i)
        if (!Dfn[i])
            dfs(i);
    for (int i = 1; i <= ST_cnt; ++i)
        for (int j = Fst[i]; j; j = Nxt[j]) {
            int &t = V[j];
            if (Bel[i] != Bel[t])
                fuck_edge(Bel[i], Bel[t]), ++Ind[Bel[t]];
        }
    for (int i = 1; i <= N; ++i)
        Mk[Bel[i]] = 1;
    
    int hd = 1, tl = 1, ans = -1;
    for (int i = 1; i <= SCC; ++i)
        if (!Ind[i])
            Q[tl++] = i;
    while (hd < tl) {
        int p = Q[hd++];
        if (Mk[p]) {
            ans = p;
            break;
        }
        for (int i = Fk_fst[p]; i; i = Fk_nxt[i]) {
            int &t = Fk_v[i];
            if (!--Ind[t])
                Q[tl++] = t;
        }
    }
    for (int i = 1; i <= N; ++i)
        printf("%d
", Bel[i] == ans);
    return 0;
}
原文地址:https://www.cnblogs.com/ghcred/p/10169022.html