• 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],然后把这个点插入路径即可.

    1. if (scc[cc].size() == 1) return;
    2. int s = scc[cc][0], t = s;
    3. for (int i = 1; i < scc[cc].size(); ++i) {
    4. int x = scc[cc][i];
    5. if (g[t][x]) nxt[t] = x, t = x;
    6. else if (g[x][s]) nxt[x] = s, s = x;
    7. else {
    8. for (int j = s; j != t; j = nxt[j])
    9. if (g[j][x] && g[x][nxt[j]]) {
    10. nxt[x] = nxt[j], nxt[j] = x;
    11. break;
    12. }
    13. }
    14. }

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

    具体的步骤如下:

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

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

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

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

    1. t = 0;
    2. for (int i = nxt[s]; i; i = nxt[i])
    3. if (t) {
    4. if (g[i][s]) t = i;
    5. else {
    6. for (int j = s, k = nxt[s]; j != t; j = k, k = nxt[k])
    7. if (g[i][k]) {
    8. nxt[j] = nxt[t], nxt[t] = s, s = k, t = i;
    9. break;
    10. }
    11. }
    12. }else if (g[i][s]) t = i;
    13. nxt[t] = s;

    完整代码:

    时间复杂度O(n^2)

    1. #include
    2. #define int long long
    3. #define IOS ios::sync_with_stdio(false), cin.tie(0)
    4. #define ll long long
    5. // #define double long double
    6. #define ull unsigned long long
    7. #define PII pair
    8. #define PDI pair
    9. #define PDD pair
    10. #define debug(a) cout << #a << " = " << a << endl
    11. #define point(n) cout << fixed << setprecision(n)
    12. #define all(x) (x).begin(), (x).end()
    13. #define mem(x, y) memset((x), (y), sizeof(x))
    14. #define lbt(x) (x & (-x))
    15. #define SZ(x) ((x).size())
    16. #define inf 0x3f3f3f3f
    17. #define INF 0x3f3f3f3f3f3f3f3f
    18. 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;
    19. using namespace std;
    20. const int N = 2e3 + 10;
    21. int n, g[N][N];
    22. int dfn[N], low[N], tim, c[N], cnt, instk[N], stk[N], top, in[N], pos[N], nxt[N];
    23. vector<int> son[N], scc[N], sson[N], ans[N];
    24. void tarjan(int x) {
    25. dfn[x] = low[x] = ++tim;
    26. stk[++top] = x; instk[x] = 1;
    27. for (int i = 1, y; i <= n; ++i) {
    28. if (!g[x][y = i]) continue;
    29. if (!dfn[y]) {
    30. tarjan(y);
    31. low[x] = min(low[x], low[y]);
    32. }else if (instk[y]) low[x] = min(low[x], dfn[y]);
    33. }
    34. if (dfn[x] == low[x]) {
    35. ++cnt; int y;
    36. do {
    37. y = stk[top--];
    38. c[y] = cnt; scc[cnt].emplace_back(y);
    39. } while (x != y);
    40. }
    41. }
    42. void solve(int cc) {
    43. if (scc[cc].size() == 1) return;
    44. int s = scc[cc][0], t = s;
    45. for (int i = 1; i < scc[cc].size(); ++i) {
    46. int x = scc[cc][i];
    47. if (g[t][x]) nxt[t] = x, t = x;
    48. else if (g[x][s]) nxt[x] = s, s = x;
    49. else {
    50. for (int j = s; j != t; j = nxt[j])
    51. if (g[j][x] && g[x][nxt[j]]) {
    52. nxt[x] = nxt[j], nxt[j] = x;
    53. break;
    54. }
    55. }
    56. }
    57. t = 0;
    58. for (int i = nxt[s]; i; i = nxt[i])
    59. if (t) {
    60. if (g[i][s]) t = i;
    61. else {
    62. for (int j = s, k = nxt[s]; j != t; j = k, k = nxt[k])
    63. if (g[i][k]) {
    64. nxt[j] = nxt[t], nxt[t] = s, s = k, t = i;
    65. break;
    66. }
    67. }
    68. }else if (g[i][s]) t = i;
    69. nxt[t] = s;
    70. }
    71. signed main() {
    72. qio >> n;
    73. for (int i = 2; i <= n; ++i)
    74. for (int j = 1, x; j <= i - 1; ++j) {
    75. qio >> x;
    76. if (x) son[j].emplace_back(i), g[j][i] = 1;
    77. else son[i].emplace_back(j), g[i][j] = 1;
    78. }
    79. for (int i = 1; i <= n; ++i)
    80. if (!dfn[i]) tarjan(i);
    81. for (int i = 1; i <= cnt; ++i)
    82. for (int j : scc[i])
    83. for (int k = 1; k <= n; ++k)
    84. if (g[j][k] && i != c[k])
    85. sson[i].emplace_back(c[k]), ++in[c[k]];
    86. // for (int i = 1; i <= n; ++i)
    87. // for (int x : son[i])
    88. // if (c[x] != c[i])
    89. // sson[c[i]].emplace_back(c[x]), ++in[c[x]];
    90. queue<int> q;
    91. // top = 0;
    92. for (int i = 1; i <= cnt; ++i) if (!in[i]) q.emplace(i);
    93. while (q.size()) {
    94. int x = q.front(); q.pop();
    95. stk[++top] = x; pos[x] = top;
    96. for (int i : sson[x]) if (--in[i] == 0) q.emplace(i);
    97. }
    98. for (int i = 1; i <= top; ++i) solve(stk[i]);
    99. for (int i = 1; i <= n; ++i) {
    100. int lst = i, now = pos[c[i]];
    101. while (1) {
    102. if (scc[stk[now]].size() == 1) {
    103. ans[i].emplace_back(lst);
    104. if (now == top) break;
    105. lst = scc[stk[++now]][0];
    106. continue;
    107. }
    108. ans[i].emplace_back(lst);
    109. for (int x = nxt[lst]; x != lst; x = nxt[x])
    110. ans[i].emplace_back(x);
    111. if (now == top) break;
    112. lst = scc[stk[++now]][0];
    113. }
    114. }
    115. for (int i = 1; i <= n; ++i) {
    116. qio << ans[i].size() << " ";
    117. for (int x : ans[i]) qio << x << " ";
    118. qio << "\n";
    119. }
    120. }

  • 相关阅读:
    【多线程】创建线程池有几种方式
    R语言使用read_csv函数读取csv数据、并通过encoding参数指定编码格式、通过设置width参数显示dataframe的所有数据行
    智能硬件适配测试
    docker常用基本命令
    Java的基础知识
    python画板
    Qt中简单的并发方式QtConcurrent::run() 方法
    嵌入式开发:使用FILL提高代码完整性
    SSM篇目录总结
    设计模式之【门面模式(外观模式)】
  • 原文地址:https://blog.csdn.net/CK1513710764/article/details/126524052