【gdsoi2018 day3】谁是冠军

题目大意:

不说。

题解:

如果x能胜y,则x往y连一条边。

如果一个点能够遍历所有的点,显然它就是可以的。

用tarjan缩一下强联通分量,入度为0的那个分量就是答案。

这样就有60分。

用主席树优化一下连边。

大概是线段树区间会下放到log个完整区间,直接往那些区间连边。

那些区间往包含的点连边。

注意插入有时效性,所以要用主席树的那种思想,新开点。

#include
#include
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const int N = 1e5 + 5, M = 1e7 + 2e6, D = 6e6;

int n, td, c[N], d[M];

struct node {
    int i, a, b, c;
} a[N];

int c1(node a, node b) {return a.a < b.a;}
int c2(node a, node b) {return a.b < b.b;}
int c3(node a, node b) {return a.c < b.c;}

int final[D], next[M], to[M], tot, rt;

void link(int x, int y) {next[++ tot] = final[x], to[tot] = y, final[x] = tot;}

int pl, pr, px;

struct tree {
    int l, r;
} t[D];

void fi(int &i, int x, int y) {
    if(y < pl || x > pr) return;
    if(!i) return;
    if(x >= pl && y <= pr) {link(px, i); return;}
    int m = x + y >> 1;
    fi(t[i].l, x, m); fi(t[i].r, m + 1, y);
}

void add(int &i, int x, int y) {
    if(y < pl || x > pr) return;
    t[++ td] = t[i];
    link(td, px); if(i) link(td, i);
    i = td;
    if(x == y) return;
    int m = x + y >> 1;
    add(t[i].l, x, m); add(t[i].r, m + 1, y);
}

void GG() {
    rt = 0;
    fo(i, 1, n) {
        pl = 1; pr = c[i] - 1; px = d[i];
        fi(rt, 1, n);
        pl = pr = c[i]; px = d[i];
        add(rt, 1, n);
    }
}

int win(int i, int j) {
    return (a[i].a > a[j].a) + (a[i].b > a[j].b) + (a[i].c > a[j].c);
}

int dfn[D], low[D], tt, fa[D], tf, bd[D];

int zx[M], zi[M], z0;
bool bz[M];

void dg(int x) {
    zx[z0 = 1] = x; bz[z0] = 0;
    int ti;
    while(z0) {
        ti = 0; x = zx[z0];
        if(bz[z0]) {
            int i = zi[z0]; low[x] = min(low[x], low[to[i]]);
            for(i = next[i]; i; i = next[i]) {
                int y = to[i];
                if(!dfn[y]) {
                    zi[z0] = i; bz[z0] = 1;
                    ++ z0;
                    zx[z0] = y; bz[z0] = 0;
                    ti = 1; break;
                } else {
                    if(bd[y]) low[x] = min(low[x], dfn[y]);
                }
            }
            if(ti) continue;
            if(dfn[x] == low[x]) {
                tf ++;
                do {
                    fa[d[d[0]]] = tf; bd[d[d[0]]] = 0;
                } while(d[d[0] --] != x);
            }
        } else {
            dfn[x] = low[x] = ++ tt;
            bd[x] = 1; d[++ d[0]] = x;
            for(int i = final[x]; i; i = next[i]) {
                int y = to[i];
                if(!dfn[y]) {
                    zi[z0] = i; bz[z0] = 1;
                    ++ z0;
                    zx[z0] = y; bz[z0] = 0;
                    ti = 1; break;
                } else {
                    if(bd[y]) low[x] = min(low[x], dfn[y]);
                }
            }
            if(ti) continue;
            if(dfn[x] == low[x]) {
                tf ++;
                do {
                    fa[d[d[0]]] = tf; bd[d[d[0]]] = 0;
                } while(d[d[0] --] != x);
            }
        }
        z0 --;
    }
}

int main() {
    freopen("champion.in", "r", stdin);
    freopen("champion.out", "w", stdout);
    scanf("%d", &n);
    fo(i, 1, n) a[i].i = i, scanf("%d %d %d", &a[i].a, &a[i].b, &a[i].c);
    sort(a + 1, a + n + 1, c1);
    fo(i, 1, n) a[i].a = i;
    sort(a + 1, a + n + 1, c2);
    fo(i, 1, n) a[i].b = i;
    sort(a + 1, a + n + 1, c3);
    fo(i, 1, n) a[i].c = i;

    td = n;

    sort(a + 1, a + n + 1, c1);
    fo(i, 1, n) c[i] = a[i].b, d[i] = a[i].i;
    GG();
    sort(a + 1, a + n + 1, c2);
    fo(i, 1, n) c[i] = a[i].c, d[i] = a[i].i;
    GG();
    sort(a + 1, a + n + 1, c3);
    fo(i, 1, n) c[i] = a[i].a, d[i] = a[i].i;
    GG();
    fo(i, 1, n) d[i] = i;
    fo(i, 1, n - 1) if(win(d[i - 1], d[i])) swap(d[i - 1], d[i]);
    int g = a[d[n]].i;
    dg(g);
    fo(i, 1, n) if(fa[i] == fa[g])
        printf("%d\n", i);
}

你可能感兴趣的:(线段树,Tarjan)