Cod sursa(job #23201)

Utilizator bogdan2412Bogdan-Cristian Tataroiu bogdan2412 Data 28 februarie 2007 14:36:03
Problema Santa Scor 60
Compilator cpp Status done
Runda Arhiva de probleme Marime 6.68 kb
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

#define MAXN 45005

int N, M;
int S, D, Qs;
vector<int> con[MAXN];
int alt[MAXN], p[MAXN], low[MAXN], art[MAXN], u[MAXN];

vector<int> nodincomp[MAXN];
vector< vector<int> > compnod;
vector< vector< pair<int, int> > > compmuc;
vector<bool> usecomp, usenod;

vector< pair<int, int> > st;

/*
 * introduce muchia (a, b) in stiva
 */
void push(int a, int b) { st.push_back( make_pair(a, b) ); }

/*
 * extrage componenta biconexa
 * scoate muchii din stiva pana ajunge la (a, b)
 */
void popcomp(int a, int b)
{
	vector<int> noduri;
	vector< pair<int, int> > muchii;

	noduri.clear(); muchii.clear();

	for (; 1; st.pop_back())
	{
		noduri.push_back( st.back().first );
		noduri.push_back( st.back().second );
		muchii.push_back( st.back() );

		if ( st.back() == make_pair(a, b) )
		{
			st.pop_back();
			break;
		}
	}

	sort(noduri.begin(), noduri.end());
	sort(muchii.begin(), muchii.end());

	noduri.resize( unique(noduri.begin(), noduri.end()) - noduri.begin() );
	muchii.resize( unique(muchii.begin(), muchii.end()) - muchii.begin() );

	compnod.push_back( noduri );
	compmuc.push_back( muchii );
	usecomp.push_back( false );			//aflu mai tarziu daca componenta respectiva e pe drumu piticilor :)
							//drumul piticilor va fi un sir de componente biconexe si puncte de articulatie
							//fiecare punct de articulatie va aparea doar o data in drum si astfel se va trece
							//prin maxim 2 componente biconexe legate de orice punct de articulatie
	for (size_t i = 0; i < noduri.size(); i++)
		nodincomp[ noduri[i] ].push_back( (int)compnod.size() - 1 );
}

/*
 * afla componentele biconexe
 */
void dfs(int k)
{
	int NCHILD = 0;					//numarul de fii in arborele DF al nodului curent, util sa vad daca nodu 1 e critic
	u[k] = 1;
	low[k] = alt[k];
	vector<int> :: iterator it;
	for (it = con[k].begin(); it != con[k].end(); it++)
	{
		if (!u[*it])
		{
			alt[*it] = alt[k] + 1;
			p[*it] = k;
			push(k, *it);
			dfs(*it);
			NCHILD++;
			if (low[*it] < low[k])
				low[k] = low[*it];
			if (low[*it] >= alt[k])		//nodul k e critic
			{
				art[k] = 1;
				if (k == 1 && NCHILD < 2)
					art[k] = 0;
				popcomp(k, *it);	//extrage componenta biconexa
			}
		}
		else
			if (*it != p[k] && alt[*it] < alt[k])
			{
				push(k, *it);
				if (alt[*it] < low[k])
					low[k] = alt[*it];
			}
	}
}

/*
 * marcheaza componenta biconexa (cur) ca facand parte dintr-un potential drum al piticilor
 */
inline void MARK(int cur)
{
	vector< int > :: iterator it;
	usecomp[cur] = true;
	for (it = compnod[cur].begin(); it != compnod[cur].end(); it++)
		usenod[*it] = true;
}

/*
 * marcheaza nodurile ce trebuie vizitate
 */
queue<int> Q;
int START, END;						//prima si ultima componenta biconexa din drumul piticilor
vector< bool > hasS, hasD, hasQs;

int bfs(int S)
{
	for (; !Q.empty(); Q.pop());

	int i;
	vector< int > :: iterator it;

	memset(u, -1, sizeof(u));			//in u voi mentine componenta biconexa precedenta
	memset(p, -1, sizeof(p));			//in p voi mentine nodul critic care leaga componenta biconexa curenta de cea precedenta
	usenod.clear(); usenod.resize(N + 1, false);
	//gasesc pentru toate componentele biconexe daca contin cele 3 noduri importante :)
	hasS.resize( compnod.size(), false );
	hasD.resize( compnod.size(), false );
	hasQs.resize( compnod.size(), false );
	for (size_t k = 0; k < compnod.size(); k++)
		for (size_t a = 0; a < compnod[k].size(); a++)
		{
			if (compnod[k][a] == S) hasS[k] = true;
			if (compnod[k][a] == D) hasD[k] = true;
			if (compnod[k][a] == Qs) hasQs[k] = true;
		}
	//gasesc componenta biconexa din care va incepe drumul (cea care contine Qs
	//pentru a avea solutie trebuie ca Qs sa se afla in aceeasi componenta biconexa cu S sau cu D
	START = -1;
	for (size_t k = 0; k < compnod.size(); k++)
		if ((hasS[k] && hasQs[k]) || (hasD[k] && hasQs[k]))
		{
			START = k;
			break;
		}

	if (START == -1)
		return 0;
	int foundS, foundD;
	foundS = hasS[START] ? 1 : 0;
	foundD = hasD[START] ? 1 : 0;
	if (foundS && foundD)
	{
		END = START;
		MARK(START);
		return 1;
	}

	//bfs
	for (Q.push(START), u[START] = START; !Q.empty() && (!foundS || !foundD); Q.pop())
	{
		i = Q.front();
		for (size_t k = 0; k < compnod[i].size(); k++)
		{
			int n = compnod[i][k];
			for (it = nodincomp[n].begin(); it != nodincomp[n].end(); it++)
			{
				if (u[*it] != -1)
					continue;

				u[*it] = i;
				p[*it] = n;
				Q.push( *it );
				
				//verific daca am gasit componenta biconexa cu care se incheie drumul
				if (!foundD && hasD[*it])
				{
					END = *it;
					foundD = 1;
					break;
				}
				if (!foundS && hasS[*it])
				{
					END = *it;
					foundS = 1;
					break;
				}
			}
		}
	}

	if (!foundS || !foundD)
		return 0;

	//marchez nodurile care vor face parte din drum
	for (i = END; i != START; i = u[i])
		MARK(i);
	MARK(START);
	return 1;
}

vector<int> DRUM;				//solutia problemei

/*
 * genereaza drumul pentru o componenta biconexa
 */
int stb[MAXN];
int back(int comp, int last, int k)
{
	int i;
	if (k == (int)compnod[comp].size() - 1)
	{
		if (stb[k] == last || last == -1)
		{
			for (i = 1; i <= k; i++)
				DRUM.push_back( stb[i] );
			return 1;
		}
		return 0;
	}

	int cur = stb[k];
	for (i = 0; i < (int)compmuc[comp].size(); i++)
	{
		pair<int, int> muc = compmuc[comp][i];
		if (muc.first != cur && muc.second != cur)
			continue;

		int nxt = muc.first + muc.second - cur;
		if (u[nxt]) 
			continue;
		u[nxt] = 1;
		stb[k + 1] = nxt;
		if (back(comp, last, k + 1))
			return 1;
		u[nxt] = 0;
	}
	return 0;
}

/*
 * genereaza drumul cerut
 */
void makedrum()
{
	vector<int> dr;				//drumul format din componente biconexe
	vector<int> snod;			//nodul din care incepe drumul hamiltonian in componenta curenta

	int i;
	for (i = END; i != START; i = u[i])
		dr.push_back( i ),
		snod.push_back( p[i] );
	dr.push_back( START );
	snod.push_back( Qs );

	reverse( dr.begin(), dr.end() );
	reverse( snod.begin(), snod.end() );

	DRUM.push_back( snod[0] );
	memset(u, 0, sizeof(u));	//reset used nodes
	for (i = 0; i < (int)dr.size() - 1; i++)
	{
		stb[0] = snod[i];
		back(dr[i], snod[i + 1], 0);	//ar fi mers dinamica memoizata
	}
	stb[0] = snod[i];
	back(dr[i], -1, 0);

	for (i = 0; i < (int)DRUM.size(); i++)
		printf("%d ", DRUM[i]);
	printf("\n");
}

int main()
{
	freopen("santa.in", "rt", stdin);
	freopen("santa.out", "wt", stdout);

	scanf("%d %d", &N, &M);
	for (; M; M--)
	{
		int i, j;
		scanf("%d %d", &i, &j);

		con[i].push_back(j);
		con[j].push_back(i);
	}
	scanf("%d %d %d", &S, &D, &Qs);

	dfs(1);					//afla componentele biconexe
	
	if (!bfs(S))				//afla toate nodurile care trebuie atinse
	{
		printf("No chance\n");
		return 0;
	}

	int NR = 0;
	for (int i = 1; i <= N; i++)
		if (usenod[i])
			NR++;
	printf("%d\n", NR);
	makedrum();

	return 0;
}