P3561 [POI2017]Turysta(竞赛图哈密顿回路的构造+强连通分量)

思路:

首先,竞赛图在缩点之后会形成一条链,然后在同一个强联通分量内的点都具有哈密顿回路,这道题的做法就很清晰了:先找出每个连通分量的哈密顿回路,然后对于每个点,从其所在的连通分量开始走一直走到链的另外一端,就是最长的路径.

接下来我们将构造哈密顿回路:

首先构造哈密顿通路, 对于任意的竞赛图都存在哈密顿通路.

假设我们构造好了前i - 1个点的哈密顿通路.

 上面只标出与通路有关的点和边,S为起点,T为终点.那么第i个点一定至少满足下面三个条件之一:

1.i -> S

2.T-> i

3.x -> i, i -> nxt[x](x ∈[1, i - 1]).

我们可以用画图证明:

 

 容易知道,上面两种情况是不满足第一,二个前提下,对于第三个前提最坏的情况,但是我们仍能找出满足第三个前提的路径(S -> i, i -> nxt[S]和 pre[T] -> i, i -> T).

所以我们构造通路的策略是:

1.先找i -> S,如果有则让S = i.

2.再找T -> i,如果有则让T = i.

3.枚举每个点,找到j -> i, i -> nxt[j],然后把这个点插入路径即可.

if (scc[cc].size() == 1) return;
	int s = scc[cc][0], t = s;
	for (int i = 1; i < scc[cc].size(); ++i) {
		int x = scc[cc][i];
		if (g[t][x]) nxt[t] = x, t = x;
		else if (g[x][s]) nxt[x] = s, s = x;
		else {
			for (int j = s; j != t; j = nxt[j])
				if (g[j][x] && g[x][nxt[j]]) {
					nxt[x] = nxt[j], nxt[j] = x;
					break;
				}
		}
	}

接下来就是用已经构造好的哈密顿通路来构造哈密顿回路了.

具体的步骤如下:

1.如果出现i -> S,那么就把i设置新的T.由于是处于强连通分量,所以一定会出现这种情况,所以一定有终点.

2.假设起点是S,终点为T, x是路径上的其中一点,如果存在i -> x, 那么我们按照下图构造路径.

 其中紫色路径就是我们要新构造的路径,这里我们假设x的前驱是pre[x],T的后继是nxt[T],从pre[x]到nxt[T]这段路是通过其他点到达的,因为是同一个强联通分量.

我们在S到T这条路径上找出一个点x满足这个条件即可.

t = 0;
for (int i = nxt[s]; i; i = nxt[i])
	if (t) {
	    if (g[i][s]) t = i;
		else {
			for (int j = s, k = nxt[s]; j != t; j = k, k = nxt[k])
				if (g[i][k]) {
					nxt[j] = nxt[t], nxt[t] = s, s = k, t = i;
					break;
				}
		}
	}else if (g[i][s]) t = i;
nxt[t] = s;

完整代码:

时间复杂度O(n^2)

#include <bits/stdc++.h>
#define int long long                                         
#define IOS ios::sync_with_stdio(false), cin.tie(0) 
#define ll long long 
// #define double long double
#define ull unsigned long long 
#define PII pair<int, int> 
#define PDI pair<double, int> 
#define PDD pair<double, double> 
#define debug(a) cout << #a << " = " << a << endl 
#define point(n) cout << fixed << setprecision(n)
#define all(x) (x).begin(), (x).end() 
#define mem(x, y) memset((x), (y), sizeof(x)) 
#define lbt(x) (x & (-x)) 
#define SZ(x) ((x).size()) 
#define inf 0x3f3f3f3f 
#define INF 0x3f3f3f3f3f3f3f3f
namespace nqio{const unsigned R = 4e5, W = 4e5; char *a, *b, i[R], o[W], *c = o, *d = o + W, h[40], *p = h, y; bool s; struct q{void r(char &x){x = a == b && (b = (a = i) + fread(i, 1, R, stdin), a == b) ? -1 : *a++;} void f(){fwrite(o, 1, c - o, stdout); c = o;} ~q(){f();}void w(char x){*c = x;if (++c == d) f();} q &operator >>(char &x){do r(x);while (x <= 32); return *this;} q &operator >>(char *x){do r(*x); while (*x <= 32); while (*x > 32) r(*++x); *x = 0; return *this;} template<typename t> q&operator>>(t &x){for (r(y),s = 0; !isdigit(y); r(y)) s |= y == 45;if (s) for (x = 0; isdigit(y); r(y)) x = x * 10 - (y ^ 48); else for (x = 0; isdigit(y); r(y)) x = x * 10 + (y ^ 48); return *this;} q &operator <<(char x){w(x);return *this;}q &operator<< (char *x){while (*x) w(*x++); return *this;}q &operator <<(const char *x){while (*x) w(*x++); return *this;}template<typename t> q &operator<< (t x) {if (!x) w(48); else if (x < 0) for (w(45); x; x /= 10) *p++ = 48 | -(x % 10); else for (; x; x /= 10) *p++ = 48 | x % 10; while (p != h) w(*--p);return *this;}}qio; }using nqio::qio;
using namespace std;
const int N = 2e3 + 10;
int n, g[N][N];
int dfn[N], low[N], tim, c[N], cnt, instk[N], stk[N], top, in[N], pos[N], nxt[N];
vector<int> son[N], scc[N], sson[N], ans[N];
void tarjan(int x) {
	dfn[x] = low[x] = ++tim;
	stk[++top] = x; instk[x] = 1;
	for (int i = 1, y; i <= n; ++i) {
		if (!g[x][y = i]) continue;
		if (!dfn[y]) {
			tarjan(y);
			low[x] = min(low[x], low[y]);
		}else if (instk[y]) low[x] = min(low[x], dfn[y]);
	}
	if (dfn[x] == low[x]) {
		++cnt; int y;
		do {
			y = stk[top--];
			c[y] = cnt; scc[cnt].emplace_back(y);
		} while (x != y);
	}
}
void solve(int cc) {
	if (scc[cc].size() == 1) return;
	int s = scc[cc][0], t = s;
	for (int i = 1; i < scc[cc].size(); ++i) {
		int x = scc[cc][i];
		if (g[t][x]) nxt[t] = x, t = x;
		else if (g[x][s]) nxt[x] = s, s = x;
		else {
			for (int j = s; j != t; j = nxt[j])
				if (g[j][x] && g[x][nxt[j]]) {
					nxt[x] = nxt[j], nxt[j] = x;
					break;
				}
		}
	}
	t = 0;
	for (int i = nxt[s]; i; i = nxt[i])
		if (t) {
			if (g[i][s]) t = i;
			else {
				for (int j = s, k = nxt[s]; j != t; j = k, k = nxt[k])
					if (g[i][k]) {
						nxt[j] = nxt[t], nxt[t] = s, s = k, t = i;
						break;
					}
			}
		}else if (g[i][s]) t = i;
	nxt[t] = s;
}
signed main() {
	qio >> n;
	for (int i = 2; i <= n; ++i) 
		for (int j = 1, x; j <= i - 1; ++j) {
			qio >> x;
			if (x) son[j].emplace_back(i), g[j][i] = 1;
			else   son[i].emplace_back(j), g[i][j] = 1;
		}
	for (int i = 1; i <= n; ++i)
		if (!dfn[i]) tarjan(i);
	for (int i = 1; i <= cnt; ++i)
		for (int j : scc[i])
			for (int k = 1; k <= n; ++k)
				if (g[j][k] && i != c[k])
					sson[i].emplace_back(c[k]), ++in[c[k]];
	// for (int i = 1; i <= n; ++i)
		// for (int x : son[i])
			// if (c[x] != c[i]) 
				// sson[c[i]].emplace_back(c[x]), ++in[c[x]];
	queue<int> q;
	// top = 0;
	for (int i = 1; i <= cnt; ++i) if (!in[i]) q.emplace(i);
	while (q.size()) {
		int x = q.front(); q.pop();
		stk[++top] = x; pos[x] = top;
		for (int i : sson[x]) if (--in[i] == 0) q.emplace(i); 
	}
	for (int i = 1; i <= top; ++i) solve(stk[i]);
	for (int i = 1; i <= n; ++i) {
		int lst = i, now = pos[c[i]];
		while (1) {
			if (scc[stk[now]].size() == 1) {
				ans[i].emplace_back(lst);
				if (now == top) break;
				lst = scc[stk[++now]][0];
				continue;
			}
			ans[i].emplace_back(lst);
			for (int x = nxt[lst]; x != lst; x = nxt[x])
				ans[i].emplace_back(x);
			if (now == top) break;
			lst = scc[stk[++now]][0];
		}
	}
	for (int i = 1; i <= n; ++i) {
		qio << ans[i].size() << " ";
		for (int x : ans[i]) qio << x << " ";
		qio << "\n";
	}
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值