Cod sursa(job #70204)

Utilizator dominoMircea Pasoi domino Data 5 iulie 2007 05:41:37
Problema Santa Scor 100
Compilator cpp Status done
Runda Arhiva de probleme Marime 4.7 kb
#include <stdio.h>
#include <algorithm>
#include <stack>
#include <vector>

using namespace std;

#define MAX_N 45005
#define MAX_M 130005
#define MAX_BCC 15
#define FIN "santa.in"
#define FOUT "santa.out"
#define FOR(i, a, b) for (i = (a); i < (b); i++)
#define FORI(it, n) for (it = (n).begin(); it != (n).end(); it++)
#define ALL(x) (x).begin(), (x).end()
#define pb push_back
#define mp make_pair
#define sz size()
#define f first
#define s second
#define PII pair<int, int> 
#define add(x, y) T[x].pb(y), T[y].pb(x)
#define find(x) lower_bound(ALL(V), (x))-V.begin()

typedef vector<int> VI;
typedef vector< PII > edge_list;

int N, M, S, E, Q, low[MAX_N], lev[MAX_N], up[MAX_N], C[MAX_N], C2[MAX_M], ncomp, BCC_N;
char bad[MAX_N], U[MAX_N], mem[1<<MAX_BCC][MAX_BCC], BCC_G[MAX_BCC][MAX_BCC];
VI G[MAX_N], T[MAX_M], P, sol; edge_list BCC[MAX_M]; stack < PII > stk;

void DFS(int n) // find BCC's
{
    VI::iterator p;
    int a, b;

    low[n] = lev[n];
    FORI(p, G[n])
    {
        if (*p != up[n] && lev[*p] < lev[n]) stk.push(mp(*p, n));
        if (!lev[*p])
        {
            lev[*p] = lev[n]+1;
            up[*p] = n;
            DFS(*p);
            if (low[n] > low[*p]) low[n] = low[*p];
            if (low[*p] < lev[n]) continue;
            bad[n] = 1;
            ncomp++;
            do
            {
                C[a = stk.top().f] = C[b = stk.top().s] = ncomp;
                BCC[ncomp].pb(mp(a, b));
                stk.pop();
            } while ((a != *p || b != n) && (a != n || b != *p));
        }
        else
        if (*p != up[n] && low[n] > lev[*p])
            low[n] = lev[*p];
    }
}

void DFS2(int n) // DFS for tree
{
    VI::iterator p;

    U[n] = 1;
    if (n == C[E]) return;
    FORI(p, T[n])
    {
        if (U[*p]) continue;
        up[*p] = n;
        DFS2(*p);
    }
}

int works(int i, int j) // DP with memoization
{
    int k;

    if (mem[i][j] != -1) return mem[i][j] >= 0;
    mem[i][j] = -2;
    FOR (k, 0, BCC_N)
    {
        if ((i&(1<<k)) == 0 || !BCC_G[k][j] || !works(i-(1<<j), k)) continue;
        mem[i][j] = k;
        break;
    }
    return mem[i][j] >= 0;
}

void trace(int i, int j, VI V) // dump the shit
{
     if (j == MAX_BCC) return;
     trace(i-(1<<j), mem[i][j], V);
     sol.pb(V[j]);
}

void solve(edge_list e, int src, int dest) // hamiltonian cycle for each component
{
    int i, a, b;
    edge_list::iterator it; VI V;

    // relabel nodes in 0...MAX_BCC-1
    memset(BCC_G, 0, sizeof(BCC_G));
    FORI(it, e) 
    {
        a = it->f, b = it->s;
        V.pb(a); V.pb(b);
    }
    sort(ALL(V));
    V.erase(unique(ALL(V)), V.end());

    // build graph
    BCC_N = V.sz; 
    FORI(it, e)
    {
        a = it->f, b = it->s;
        a = find(a); b = find(b);
        BCC_G[a][b] = BCC_G[b][a] = 1;
    }
    src = find(src); 
    dest = dest == -1 ? -1 : find(dest);

    // do the magic
    memset(mem, -1, sizeof(mem));
    mem[1<<src][src] = MAX_BCC;
    FOR (i, 0, BCC_N)
        if (dest == -1 && works((1<<BCC_N)-1, i)) dest = i;
    works((1<<BCC_N)-1, dest);
    if (sol.sz > 0) sol.pop_back();
    trace((1<<BCC_N)-1, dest, V);
}

int same_bcc(int x, int y) // test if nodes are in same BCC
{
    VI::iterator it;

    if (C[x] == C[y]) return 1;
    if (C[x] <= ncomp && C[y] <= ncomp) return 0; // different bcc's
    FORI(it, T[C[x]])
        if (*it == C[y]) return 1;
    return 0;
}

int main(void)
{
    int i, a, b, cnt = 0;
    edge_list::iterator it;

    freopen(FIN, "r", stdin);
    freopen(FOUT, "w", stdout);

    for (scanf("%d %d", &N, &M); M > 0; M--)
    {
        scanf("%d %d", &a, &b);
        G[a].pb(b); G[b].pb(a);
    }
    scanf("%d %d %d", &S, &E, &Q);

    // start coloring [1..ncomp] - biconnected components, [ncomp+1..cnt] - articulation points
    lev[S] = 1; DFS(S);
    FOR (i, 1, N+1) cnt += (up[i] == S);
    bad[S] = cnt > 1;
    cnt = ncomp;
    FOR (i, 1, N+1) if (bad[i]) { C[i] = ++cnt; C2[cnt] = i; }

    // build tree
    FOR(i, 1, ncomp+1) FORI(it, BCC[i])
    {
        a = it->f, b = it->s;
        if (bad[a]) add(i, C[a]);
        if (bad[b]) add(i, C[b]);
    }

    // find path [S]...[E]
    memset(U, 0, sizeof(U));
    memset(up, 0, sizeof(up));
    DFS2(C[S]);
    for (i = C[E]; i > 0; P.pb(i), i = up[i]);
    if (same_bcc(S, Q)) reverse(ALL(P));

    // get the route
    FOR (i, 0, P.sz)
    {
        if (P[i] > ncomp) continue;
        solve(BCC[P[i]], i < 2 ? Q : C2[P[i-1]], i+2 < P.sz ? C2[P[i+1]] : -1);
    }

    // time to cash in
    printf("%d\n", sol.sz);
    FOR (i, 0, sol.sz) printf("%d ", sol[i]);
    putchar('\n');

    return 0;
}