Cod sursa(job #3289800)

Utilizator BledeaAlexBledea Alexandru BledeaAlex Data 28 martie 2025 16:05:15
Problema Santa Scor 0
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 7.14 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> brutePath;

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

    if(not foundE)
        brutePath.push_back(v);

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

    for(const int u : G[v])
        if(not visited[u] && not foundE)
            DFS(u);

    if(not foundE)
        brutePath.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, int startNode)
{
    if(crr == startNode)
        return;

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

    path.push_back(crr);
}

void FindPathInBiconexComp(int compId, int startNode, int endNode) /// punem in drum nodurile din intervalul (start, end) din drumul hamiltonian
                                                                   /// dar daca endNode == -1, pundem din intervalul (start, end] oricare ar fi end corect
{
    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);

    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, startNode);
                for(int i = (int)path.size() - nodesCnt + 1; i < (int)path.size(); i++) /// Schimbam nodurile in id-ul lor din graful normal
                    path[i] = oldNodeId[path[i]];
            }
    }
    else
    {
        int newEndNode = newNodeId[endNode];
        if(dp[maxMask][newEndNode].possible)
        {
            found = true;
            AddHamiltonianPath((maxMask ^ (1 << newEndNode)), dp[maxMask][newEndNode].lastNode, startNode); /// Schimbam nodurile in id-ul lor din graful normal
            for(int i = (int)path.size() - nodesCnt + 2; i < (int)path.size(); i++)
                path[i] = oldNodeId[path[i]];
        }
    }

    if(not found)
    {
        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;

    DFS_CB(S, 0, S);

    //if(not visited[E] || not visited[Q]) /// Nu exista un drum de la S la E sau Q
    //    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();

    visited.assign(N + 1, false);

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

    isCriticalNode[Q] = true; /// presupunem Q ca fiind nod critic
    if(isCriticalNode[brutePath.back()]) isCriticalNode[brutePath.back()] = false;

    brutePath.erase(unique(brutePath.begin(), brutePath.end(), [](int a, int b){
                            return not isCriticalNode[a] && not isCriticalNode[b] && comp[a] == comp[b];
                        }), brutePath.end());

    int len = brutePath.size();
    for(int i = 0; i < len; i++)
        if(isCriticalNode[brutePath[i]])
            path.push_back(brutePath[i]);
        else if(i == len - 1)
            FindPathInBiconexComp(comp[brutePath[i]], brutePath[i-1], -1);
        else
            FindPathInBiconexComp(comp[brutePath[i]], brutePath[i-1], brutePath[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;
}