bzoj3440:传球游戏

Pre

真的毒瘤题,昨天晚上以为是一道简单的图论题,今天中午没有调出来,晚上我选择看题解发现漏了一种情况。

于是

就花了一晚上调程序。

现在时间(21:31)

Solution

拆点是很容易想到的。

拓扑((Tarjan)应该也可以,没试过)找环。

之后分类。

有一个点在环内就不符合。

若两个点都不在环内,需要对图重建,之后染色判断这两个点是否有父子关系。

不知不觉就快200行代码(以下的代码有删减)。

Code

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define xx first
#define yy second
 
using namespace std;
 
const int N = 1000000 + 5, M = N << 1;
int suf[M], n;
int fr[M << 1], to[M << 1], h[M], tot;
 
inline void add (int u, int v) {
    tot++;
    fr[tot] = h[u];
    to[tot] = v;
    h[u] = tot;
}
 
inline int qian (int x) {
    x--;
    while (x <= n) {
        x += n;
    }
    return x;
}
 
inline int hou (int x) {
    x++;
    while (x > n) {
        x -= n;
    }
    return x;
}
 
queue<int> Q;
int rd[M];
bool type[M];
 
inline void Top () {
    for (int i = 1; i <= n * 2; ++i) {
        if (!rd[i]) {
            Q.push (i);
        }
    }
    while (Q.size ()) {
        int tmp = Q.front();
        Q.pop();
        type[tmp] = 1;
        rd[suf[tmp]]--;
        if (!rd[suf[tmp]]) {
            Q.push(suf[tmp]);
        }
    }
}
 
int it[M], color[M], cocnt;
bool ins[M], fatin[N];
 
inline void dfs (int u) {
    if (u <= n * 2) {
        ins[u] = 1;
    }
    int ne = u > n ? u - n : u + n;
    if (ins[ne]) {
        fatin[min (u, ne)] = 1;
    }
    for (int i = h[u]; i; i = fr[i]) {
        if (u > n * 2) {
            color[to[i]] = u - n * 2;
        }
        else {
            color[to[i]] = color[u];
        }
        dfs (to[i]);
    }
    ins[u] = 0;
}
 
inline void Rebuild () {
    for (int i = 1; i <= n * 2; ++i) {
        it[i] = type[i] ^ 1;
        type[i] ^= 1;
    }
    for (int i = 1; i <= n * 2; ++i) {
        if (it[i]) {
            cocnt++;
            for (int j = suf[i]; j != i; j = suf[j]) {
                color[j] = cocnt;
                it[j] = 0;
            }
            color[i] = cocnt;
            it[i] = 0;
        }
    }
    for (int i = 1; i <= n * 2; ++i) {
        if (type[i]) {
            continue;
        }
        if (!type[suf[i]]) {
            add (suf[i], i);
        }
        else{
            add (color[suf[i]] + n * 2, i);
        }
    }
    for (int i = 1; i <= cocnt; ++i) {
        dfs (n * 2 + i);
    }
}
 
int main () {
    memset (type, 0, sizeof (type));
    scanf ("%d", &n);
    for (int i = 1; i <= n; ++i) {
        int tmp;
        scanf ("%d", &tmp);
        if (tmp == 1) {
            suf[i] = hou (i);
            suf[i + n] = qian (i);
        }
        else if (tmp == 2) {
            suf[i] = qian (i);
            suf[i + n] = hou (i);
        }
        else if (tmp == 3) {
            suf[i] = hou (hou (i));
            suf[i + n] = qian (qian (i));
        }
        else if (tmp == 4) {
            suf[i] = qian (qian (i));
            suf[i + n] = hou (hou (i));
        }
        rd[suf[i]]++;
        rd[suf[i + n]]++;
    }
    Top ();
    Rebuild ();
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        if (type[i] || type[i + n]) {
            continue;
        }
        if (!type[i] && !type[i] && fatin[i]) {
            continue;
        }
        ans++;
    }
    printf ("%d
", ans);
    for (int i = 1; i <= n; ++i) {
        if (type[i] || type[i + n]) {
            continue;
        }
        if (!type[i] && !type[i] && fatin[i]) {
            continue;
        }
        printf ("%d", i);
        ans--;
        if (ans) {
            printf (" ");
        }
    }
    printf ("
");
    return 0;
}

Conslusion

记住有向图找环用拓扑挺方便的。

学到了染色重建图的方法,有一点细节。真的是一点

原文地址:https://www.cnblogs.com/ChiTongZ/p/11153986.html