Cod sursa(job #3289761)

Utilizator BledeaAlexBledea Alexandru BledeaAlex Data 28 martie 2025 13:42:55
Problema Santa Scor 0
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 6.79 kb
#include <bits/stdc++.h>

using namespace std;

/// OBS: Fiecare comp biconexa are cel mult 15 noduri !!!
const int NR_MAX = 18, ST_MAX = 1 << NR_MAX;

int S, E, Q;

const int INF = 1e9 + 5;

vector<bool> visited;
vector<vector<int> > G;
vector<int> level, low;
stack<int> Stack;

vector<bool> isCriticalNode;
vector<pair<int, int> > criticalEdges;
vector<vector<int> > biconex;
vector<int> comp;

void SetInput(string name)
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    (void)!freopen((name + ".in").c_str(), "r", stdin);
    (void)!freopen((name + ".out").c_str(), "w", stdout);
}

void FinishProgram()
{
    cout << "No chance\n";
    exit(0);
}

void DFS_CB(int v, int t, int root) /// Pentru a calcula comp biconexe
{
    visited[v] = true;
    low[v] = level[v] = level[t] + 1;

    Stack.push(v);

    int nrChildren = 0;

    for(const int u : G[v])
        if(u != t)
        {
            if(visited[u])
            {
                low[v] = min(low[v], level[u]);
                continue;
            }

            DFS_CB(u, v, root);
            low[v] = min(low[v], low[u]);

            nrChildren++;

            if(v != root && level[v] <= low[u])
                isCriticalNode[v] = true;

            if(level[v] < low[u])
                criticalEdges.emplace_back(v, u);

            if(level[v] <= low[u])
            {
                biconex.push_back(vector<int>(0));

                int x;
                do{
                    x = Stack.top();
                    Stack.pop();
                    biconex[biconex.size() - 1].push_back(x);
                    comp[x] = biconex.size() - 1;
                } while(x != u);

                biconex[biconex.size() - 1].push_back(v);
                comp[v] = biconex.size() - 1;
            }
        }

    if(v == root && nrChildren >= 2)
        isCriticalNode[root] = true;
}

bool foundE = false;

vector<int> compBiconexInPath, criticalNodesInPath;

void DFS(int v) /// Pentru a calcula prin ce comp biconexe trece drumul de la S la E
{
    visited[v] = true;

    if(v == E)
    {
        foundE = true;
        return;
    }

    for(const int u : G[v])
        if(not visited[u] && not foundE)
        {
            if(not foundE && comp[v] != comp[u])
            {
                compBiconexInPath.push_back(comp[u]);
                criticalNodesInPath.push_back(u);
            }

            DFS(u);

            if(not foundE && comp[v] != comp[u])
            {
                compBiconexInPath.pop_back();
                criticalNodesInPath.pop_back();
            }
        }
}

vector<int> path;

struct HamiltonNode
{
    bool possible;
    int lastNode;

    HamiltonNode() { }
    HamiltonNode(bool _possible, int _lastNode) :
        possible(_possible), lastNode(_lastNode) { }
};

HamiltonNode dp[ST_MAX][NR_MAX];

void AddHamiltonianPath(int mask, int crr)
{
    if(crr == -1)
        return;

    AddHamiltonianPath((mask ^ (1 << crr)), dp[mask][crr].lastNode);

    path.push_back(crr);
}

void FindPathInBiconexComp(int compId, int startNode, int endNode)
{
    int nodesCnt = biconex[compId].size();
    vector<vector<int>> G2(nodesCnt, vector<int>());
    unordered_map<int, int> newNodeId, oldNodeId;
    for(int i = 0; i < (int)biconex[compId].size(); i++)
    {
        int v = biconex[compId][i];

        newNodeId[v] = i;
        oldNodeId[i] = v;
    }

    for(auto [newId, oldId] : oldNodeId)
        for(const int& u : G[oldId])
            if(newNodeId.count(u) != 0)
                G2[newId].push_back(newNodeId[u]);

    assert(newNodeId.count(startNode) != 0 && (endNode == -1 || newNodeId.count(endNode) != 0));

    int newStartNode = newNodeId[startNode];
    int maxMask = (1 << nodesCnt) - 1;

    for(int mask = 0; mask <= maxMask; mask++)
        for(int v = 0; v < nodesCnt; v++)
            dp[mask][v].possible = false;

    dp[(1 << newStartNode)][newStartNode] = HamiltonNode(true, -1);

    for(int mask = 0; mask < maxMask; mask++)
        for(int v = 0; v < nodesCnt; v++)
            if(dp[mask][v].possible)
                for(const int& u : G2[v])
                    if((mask & (1 << u)) == 0)
                        dp[(mask ^ (1 << u))][u] = HamiltonNode(true, v);

    bool found = false;

    if(endNode == -1) /// Putem alege orice nod la sfarsit
    {
        for(int v = 0; v < nodesCnt && not found; v++)
            if(dp[maxMask][v].possible)
            {
                found = true;
                AddHamiltonianPath(maxMask, v);
            }
    }
    else
    {
        int newEndNode = newNodeId[endNode];
        if(dp[maxMask][newEndNode].possible)
        {
            found = true;
            AddHamiltonianPath(maxMask, newEndNode);
        }
    }

    if(found) /// Schimbam nodurile in id-ul lor din graful normal
    {
        assert((int)path.size() >= nodesCnt);

        for(int i = (int)path.size() - nodesCnt; i < (int)path.size(); i++)
            path[i] = oldNodeId[path[i]];
    }
    else /// Nu putem indeplini cerinta
        FinishProgram();
}

void Solve()
{
    int N, M;

    cin >> N >> M;

    G.resize(N + 1);
    visited.resize(N + 1, false);
    isCriticalNode.resize(N + 1, false);
    level.resize(N + 1, INF);
    low.resize(N + 1, INF);
    comp.resize(N + 1, -1);

    for(int i = 1, u , v; i <= M; i++)
    {
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }

    cin >> S >> E >> Q;

    assert(S != Q && E != Q);

    DFS_CB(S, 0, S);

    if(not visited[E]) /// Nu exista un drum de la S la E
        FinishProgram();

    if(isCriticalNode[S] || isCriticalNode[Q]) /// S sau Q sunt noduri critice
        FinishProgram();

    if(find(biconex[comp[S]].begin(), biconex[comp[S]].end(), Q) == biconex[comp[S]].end()) /// S si Q NU sunt in aceeasi comp biconexa
        FinishProgram();

    compBiconexInPath.push_back(comp[Q]);
    criticalNodesInPath.push_back(Q); /// punem Q ca nod critic in drum deoarece din el incepem drumul de la Q la E
    visited.assign(N + 1, false);

    DFS(Q); /// DFS ca sa gasim prin ce comp biconexe trece drumul de la Q sa E

    for(int i = 0; i < (int)compBiconexInPath.size(); i++)
        if(i == (int)compBiconexInPath.size() - 1)
            FindPathInBiconexComp(compBiconexInPath[i], criticalNodesInPath[i], -1);
        else
            FindPathInBiconexComp(compBiconexInPath[i], criticalNodesInPath[i], criticalNodesInPath[i+1]);

    path.erase(unique(path.begin(), path.end()), path.end());

    cout << path.size() << '\n';

    for(const int& v : path)
        cout << v << ' ';
    cout << '\n';
}

int main()
{
    SetInput("santa");

    Solve();

    return 0;
}