ProblemSet

签到题就不写了。

C. Distinct Substrings

先对原串建出SAM,map存边。

由于这题相当于添加一个字符再删除这个字符,添加下一个字符,所以每次都暴力跳后缀链接是复杂度是错的。

从 \(last\) 向上跳的时候,遍历出边,把每个拥有数字 \(c\) 的出边的第一个点编号记下来,由于后缀自动机的边数是线性的,这样复杂度就是对的 \(O(n\log n)\) 虽然 \(5e6\) 但数据水还是跑过去了。

  1. #include <iostream>
  2. #include <map>
  3. #include <cstdio>
  4. #include <cstring>
  5. #include <algorithm>
  6. #include <fstream>
  7. typedef long long LL;
  8. typedef unsigned long long uLL;
  9. #define Int __int128
  10. #define SZ(x) ((int)x.size())
  11. #define ALL(x) (x).begin(), (x).end()
  12. #define MP(x, y) std::make_pair(x, y)
  13. #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
  14. #define GO cerr << "GO" << endl;
  15. using namespace std;
  16. inline void proc_status()
  17. {
  18. ifstream t("/proc/self/status");
  19. cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
  20. }
  21. template<class T> inline T read()
  22. {
  23. register int x = 0; register int f = 1; register char c;
  24. while (!isdigit(c = getchar())) if (c == '-') f = -1;
  25. while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
  26. return x * f;
  27. }
  28. template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
  29. template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
  30. const int maxN = (int) 1e6;
  31. const int mod = (int) 1e9 + 7;
  32. int pw[maxN + 2];
  33. int ncnt, last, n, m;
  34. struct Status
  35. {
  36. int len, link;
  37. map<int, int> ch;
  38. } st[2 * maxN];
  39. void init() { ncnt = 0, last = 0, st[0].len = 0, st[0].link = -1; }
  40. void insert(int c)
  41. {
  42. int cur = ++ncnt;
  43. int p = last;
  44. st[cur].len = st[last].len + 1;
  45. while (p != -1 and !st[p].ch.count(c))
  46. {
  47. st[p].ch[c] = cur;
  48. p = st[p].link;
  49. }
  50. if (p == -1)
  51. st[cur].link = 0;
  52. else
  53. {
  54. int q = st[p].ch[c];
  55. if (st[q].len == st[p].len + 1)
  56. st[cur].link = q;
  57. else
  58. {
  59. int clone = ++ncnt;
  60. st[clone] = st[q];
  61. st[clone].len = st[p].len + 1;
  62. while (p != -1 and st[p].ch[c] == q)
  63. {
  64. st[p].ch[c] = clone;
  65. p = st[p].link;
  66. }
  67. st[cur].link = st[q].link = clone;
  68. }
  69. }
  70. last = cur;
  71. }
  72. void Clear()
  73. {
  74. for (register int i = 0; i <= ncnt; ++i)
  75. st[i].ch.clear(), st[i].len = st[i].link = 0;
  76. ncnt = last = 0;
  77. }
  78. void Init()
  79. {
  80. pw[0] = 1;
  81. for (register int i = 1; i <= m; ++i) pw[i] = 3ll * pw[i - 1] % mod;
  82. }
  83. void Solve()
  84. {
  85. int ans = 0;
  86. static int place[maxN + 2];
  87. fill(place + 1, place + 1 + m, -1);
  88. int L = st[last].len + 1, p = last;
  89. while (p != -1)
  90. {
  91. for (map<int, int>::iterator it = st[p].ch.begin(); it != st[p].ch.end(); ++it)
  92. {
  93. if (place[it->first] < 0)
  94. place[it->first] = p;
  95. }
  96. p = st[p].link;
  97. }
  98. for (register int i = 1; i <= m; ++i)
  99. {
  100. if (place[i] == -1)
  101. ans ^= (LL) L * pw[i] % mod;
  102. else
  103. ans ^= (LL) (L - st[place[i]].len - 1) * pw[i] % mod;
  104. }
  105. cout << ans << endl;
  106. }
  107. int main()
  108. {
  109. #ifndef ONLINE_JUDGE
  110. freopen("C.in", "r", stdin);
  111. freopen("C.out", "w", stdout);
  112. #endif
  113. while (scanf("%d%d", &n, &m) != EOF)
  114. {
  115. init();
  116. Init();
  117. for (register int i = 1, x; i <= n; ++i)
  118. x = read<int>(), insert(x);
  119. Solve();
  120. Clear();
  121. }
  122. return 0;
  123. }

D. Modulo Nine

设 \(dp[i][j][k]\) 表示填了前 \(i\) 个位置,最近的一个含3这个因子的数在 \(k\),次近的在 \(j\)的方案数。

那么对每个点求出一个最近的需要满足限制的左端点(没有就默认为0),如果状态 \(dp[i][j][k]\) 满足 j和k都在i最近的左端点内,那么这个状态就是合法的,接下考虑i+1位填 \(\{0, 9\}\) 或 \(\{3, 6\}\) 或 \(\{1, 2, 4, 5, 7, 8\}\),刷表转移即可。

Code

  1. #include <iostream>
  2. #include <cstring>
  3. #include <cstdio>
  4. using namespace std;
  5. void chkmax(int &x, int y) { x < y ? x = y : 0; }
  6. const int maxN = 50;
  7. const int mod = (int) 1e9 + 7;
  8. int n, m;
  9. int L[maxN + 2];
  10. int dp[maxN + 2][maxN + 2][maxN + 2];
  11. void pls(int &x, int y)
  12. {
  13. x += y;
  14. if (x >= mod) x -= mod;
  15. if (x < 0) x += mod;
  16. }
  17. int main()
  18. {
  19. freopen("D.in", "r", stdin);
  20. freopen("D.out", "w", stdout);
  21. while (scanf("%d%d", &n, &m) != EOF)
  22. {
  23. memset(L, 0, sizeof L);
  24. for (int i = 1; i <= m; ++i)
  25. {
  26. int l, r;
  27. scanf("%d%d", &l, &r);
  28. chkmax(L[r], l);
  29. }
  30. memset(dp, 0, sizeof dp);
  31. dp[0][0][0] = 1;
  32. for (int i = 0; i < n; ++i)
  33. for (int j = 0; j <= i; ++j)
  34. for (int k = j; k <= i; ++k)
  35. {
  36. if (!dp[i][j][k]) continue;
  37. if (L[i] <= j and L[i] <= k)
  38. {
  39. int curr = dp[i][j][k];
  40. // {1, 2, 4, 5, 7, 8}
  41. pls(dp[i + 1][j][k], 6ll * curr % mod);
  42. // {3, 6}
  43. pls(dp[i + 1][k][i + 1], 2ll * curr % mod);
  44. // {0, 9}
  45. pls(dp[i + 1][i + 1][i + 1], 2ll * curr % mod);
  46. }
  47. }
  48. int ans = 0;
  49. for (int i = L[n]; i <= n; ++i)
  50. for (int j = i; j <= n; ++j)
  51. pls(ans, dp[n][i][j]);
  52. printf("%d\n", ans);
  53. }
  54. }

G. 字典序

参考了zsy的题解:

不难发现只要考虑相邻两行的限制,如果满足任意相邻两行满足,那么就是合法的,即 \(\forall i\in[1, n - 1],j\in[2,m],\exists k < j\) 满足 \(a[i][j] > a[i + 1][j]\) 且 \(a[i][k] < a[i + 1][k]\)。

这个限制可以抽象成 \(1\le i\le n - 1\),\(\{1,2\cdots m\}\) 的两个子集 \(A[i]\) 和 \(B[i]\),需要满足最终的排列中 \(B[i]\) 中的每一个元素都在至少一个 \(A[i]\) 中元素的后面。

那么每个点(列)会有些限制,这些限制的个数就是被多少个B集合包含,因为B集合是被A集合限制的,当限制个数为0时,我们就可以把它加入到排列中了。

  1. 4 3 3
  2. 1 5 1
  3. 1 5 1
  4. 3 5 2

比如上面这个:\(A[1] = \{2\},A[2] = \{\}, A[3]=\{1,3\}\), \(B[1] = \{1, 3\}, B[2] = \{\}, B[3] = \{\}\)

我们从前往后构造排列,若构造了 \(A[x]\) 中的数,那么对应的 \(B[x]\) 所包含的列的限制就会-1,所以每次找到最小的限制数为0的列,作为当前构造排列的最后一个,然后把它所在 \(A[x]\) 集合对应的 \(B[x]\) 集合中的列的限制-1,再将 \(A[x]\) 和 \(B[x]\) 删掉。

Code

  1. #include <iostream>
  2. #include <cstring>
  3. #include <cstdio>
  4. #include <vector>
  5. using namespace std;
  6. const int maxN = 2000;
  7. int n, m, a[maxN + 2][maxN + 2];
  8. int limits[maxN + 2];
  9. vector<int> B_to_Lie[maxN + 2];
  10. vector<int> Lie_to_A[maxN + 2];
  11. int main()
  12. {
  13. freopen("G.in", "r", stdin);
  14. freopen("G.out", "w", stdout);
  15. while (scanf("%d%d", &n, &m) != EOF)
  16. {
  17. memset(limits, 0, sizeof limits);
  18. for (int i = 1; i < n; ++i) B_to_Lie[i].clear();
  19. for (int i = 1; i <= m; ++i) Lie_to_A[i].clear();
  20. for (int i = 1; i <= n; ++i)
  21. for (int j = 1; j <= m; ++j)
  22. scanf("%d", &a[i][j]);
  23. for (int i = 1; i < n; ++i)
  24. for (int j = 1; j <= m; ++j)
  25. if (a[i][j] < a[i + 1][j])
  26. Lie_to_A[j].push_back(i);
  27. else if (a[i][j] > a[i + 1][j])
  28. {
  29. limits[j]++;
  30. B_to_Lie[i].push_back(j);
  31. }
  32. int tot = 0;
  33. static int ans[maxN + 2];
  34. for (int t = 1; t <= m; ++t)
  35. {
  36. int p = 0;
  37. for (int i = 1; i <= m; ++i)
  38. if (!limits[i])
  39. {
  40. p = i;
  41. limits[i] = 0x3f3f3f3f;
  42. break;
  43. }
  44. if (!p)
  45. break;
  46. ans[++tot] = p;
  47. for (int i = 0; i < (int) Lie_to_A[p].size(); ++i)
  48. {
  49. int A = Lie_to_A[p][i];
  50. for (int j = 0; j < (int) B_to_Lie[A].size(); ++j)
  51. {
  52. int Lie = B_to_Lie[A][j];
  53. limits[Lie]--;
  54. }
  55. B_to_Lie[A].clear();
  56. }
  57. Lie_to_A[p].clear();
  58. }
  59. if (tot == m)
  60. for (int i = 1; i <= m; ++i)
  61. if (i != m) printf("%d ", ans[i]);
  62. else printf("%d\n", ans[i]);
  63. else
  64. puts("-1");
  65. }
  66. return 0;
  67. }

H. 有向图

设 \(E[u]\) 为走无限次后期望经过 \(u\) 多少次,由于经过n+1...n+m的次数为1,所以此时n+1...n+m点期望就是概率。

列出方程高消即可。

Code

  1. #include <iostream>
  2. #include <cstring>
  3. #include <cstdio>
  4. #define LL long long
  5. using namespace std;
  6. const int maxN = 500;
  7. const int mod = (int) 1e9 + 7;
  8. LL qpow(LL a, LL b)
  9. {
  10. LL ans = 1;
  11. while (b)
  12. {
  13. if (b & 1)
  14. ans = ans * a % mod;
  15. a = a * a % mod;
  16. b >>= 1;
  17. }
  18. return ans;
  19. }
  20. int n, m;
  21. int a[maxN + 2][maxN + 2], P[maxN + 2][maxN + 2];
  22. int main()
  23. {
  24. freopen("H.in", "r", stdin);
  25. freopen("H.out", "w", stdout);
  26. while (scanf("%d%d", &n, &m) != EOF)
  27. {
  28. memset(a, 0, sizeof a);
  29. memset(P, 0, sizeof P);
  30. int inv = qpow(10000, mod - 2);
  31. for (int i = 1; i <= n; ++i)
  32. for (int j = 1; j <= n + m; ++j)
  33. {
  34. scanf("%d", &P[i][j]);
  35. P[i][j] = 1ll * P[i][j] * inv % mod;
  36. }
  37. for (int i = 1; i <= n; ++i)
  38. {
  39. for (int j = 1; j <= n; ++j)
  40. {
  41. a[i][j] = P[j][i];
  42. if (i == j) a[i][i]--;
  43. (a[i][j] += mod) %= mod;
  44. }
  45. if (i == 1) a[1][n + 1] = mod - 1;
  46. else a[i][n + 1] = 0;
  47. }
  48. for (int i = 1; i <= n; ++i)
  49. {
  50. for (int j = 1; j <= n; ++j)
  51. if (i != j)
  52. {
  53. int mul = 1ll * a[j][i] * qpow(a[i][i], mod - 2) % mod;
  54. for (int k = 1; k <= n + 1; ++k)
  55. {
  56. a[j][k] -= 1ll * a[i][k] * mul % mod;
  57. a[j][k] %= mod;
  58. (a[j][k] += mod) %= mod;
  59. }
  60. }
  61. }
  62. for (int i = 1; i <= n; ++i)
  63. a[i][n + 1] = (1ll * a[i][n + 1] * qpow(a[i][i], mod - 2) % mod + mod) % mod;
  64. for (int i = 1 + n; i <= n + m; ++i)
  65. {
  66. int ans = 0;
  67. for (int j = 1; j <= n; ++j)
  68. (ans += 1ll * a[j][n + 1] * P[j][i] % mod) %= mod;
  69. printf("%d", (ans + mod) % mod);
  70. if (i != n + m)
  71. putchar(' ');
  72. }
  73. putchar('\n');
  74. }
  75. }

J. Parity of Tuples (Easy)

首先对每一行单独考虑贡献再加起来。

接下来都是对一行考虑:

由于要求and x后二进制下都是奇数个1,所以设 \(f[i][S]\) 为考虑了 \(x\) 的前i位,这一行每个数二进制下1个数的奇偶性二进制状态为S对答案的贡献。

对于 \(3^x\) 将x拆成二进制相加,转移时乘上即可。

Code

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. using namespace std;
  5. const int maxN = (int) 1e4, maxM = 30, maxK = 30;
  6. const int mod = (int) 1e9 + 7;
  7. int n, m, k;
  8. int b[maxK + 2], pw[maxK + 2];
  9. int a[maxN + 2][maxM + 2];
  10. int f[maxK + 2][1 << maxM];
  11. int main()
  12. {
  13. freopen("J.in", "r", stdin);
  14. freopen("J.out", "w", stdout);
  15. while (scanf("%d%d%d", &n, &m, &k) != EOF)
  16. {
  17. for (int i = 1; i <= n; ++i)
  18. for (int j = 0; j < m; ++j)
  19. scanf("%d", &a[i][j]);
  20. int ans = 0;
  21. for (int t = 1; t <= n; ++t)
  22. {
  23. for (int i = 0; i <= k; ++i)
  24. {
  25. b[i] = 0;
  26. for (int j = 0; j < m; ++j)
  27. b[i] |= (a[t][j] >> i & 1) << j;
  28. }
  29. pw[0] = 3;
  30. for (int i = 1; i <= k; ++i)
  31. pw[i] = 1ll * pw[i - 1] * pw[i - 1] % mod;
  32. memset(f, 0, sizeof f);
  33. f[0][0] = 1;
  34. for (int i = 0; i < k; ++i)
  35. for (int S = 0; S < (1 << m); ++S)
  36. if (f[i][S])
  37. {
  38. (f[i + 1][S ^ b[i]] += 1ll * f[i][S] * pw[i]) %= mod;
  39. (f[i + 1][S] += f[i][S]) %= mod;
  40. }
  41. (ans += f[k][(1 << m) - 1]) %= mod;
  42. }
  43. printf("%d\n", (ans + mod) % mod);
  44. }
  45. }

K. 双向链表练习题

deque 启发式合并。

Code

  1. #include <iostream>
  2. #include <queue>
  3. #include <cstdio>
  4. #include <cstring>
  5. #include <algorithm>
  6. #include <fstream>
  7. typedef long long LL;
  8. typedef unsigned long long uLL;
  9. #define SZ(x) ((int)x.size())
  10. #define ALL(x) (x).begin(), (x).end()
  11. #define MP(x, y) std::make_pair(x, y)
  12. #define DE(x) cerr << x << endl;
  13. #define debug(...) fprintf(stderr, __VA_ARGS__)
  14. #define GO cerr << "GO" << endl;
  15. #define rep(i, a, b) for (register int (i) = (a); (i) <= (b); ++(i))
  16. using namespace std;
  17. inline void proc_status()
  18. {
  19. ifstream t("/proc/self/status");
  20. cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
  21. }
  22. inline int read()
  23. {
  24. register int x = 0; register int f = 1; register char c;
  25. while (!isdigit(c = getchar())) if (c == '-') f = -1;
  26. while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
  27. return x * f;
  28. }
  29. template<class T> inline void write(T x)
  30. {
  31. static char stk[30]; static int top = 0;
  32. if (x < 0) { x = -x, putchar('-'); }
  33. while (stk[++top] = x % 10 xor 48, x /= 10, x);
  34. while (putchar(stk[top--]), top);
  35. }
  36. template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
  37. template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
  38. const int maxN = 2e5;
  39. deque<int> q[maxN + 2];
  40. int n, m;
  41. bool rev[maxN + 2];
  42. int size(int x) { return q[x].size(); }
  43. int front(int x)
  44. {
  45. if (rev[x]) return q[x].back();
  46. else return q[x].front();
  47. }
  48. int back(int x)
  49. {
  50. if (rev[x]) return q[x].front();
  51. else return q[x].back();
  52. }
  53. void push_front(int x, int y)
  54. {
  55. if (rev[x]) q[x].push_back(y);
  56. else q[x].push_front(y);
  57. }
  58. void push_back(int x, int y)
  59. {
  60. if (rev[x]) q[x].push_front(y);
  61. else q[x].push_back(y);
  62. }
  63. void pop_back(int x)
  64. {
  65. if (rev[x]) q[x].pop_front();
  66. else q[x].pop_back();
  67. }
  68. void pop_front(int x)
  69. {
  70. if (rev[x]) q[x].pop_back();
  71. else q[x].pop_front();
  72. }
  73. int main()
  74. {
  75. #ifndef ONLINE_JUDGE
  76. freopen("K.in", "r", stdin);
  77. freopen("K.out", "w", stdout);
  78. #endif
  79. while (scanf("%d%d", &n, &m) != EOF)
  80. {
  81. for (int i = 1; i <= n; ++i)
  82. q[i].clear(), q[i].push_front(i), rev[i] = 0;
  83. for (int i = 1; i <= m; ++i)
  84. {
  85. int a, b;
  86. scanf("%d%d", &a, &b);
  87. if (q[a].size() > q[b].size())
  88. {
  89. while (size(b))
  90. {
  91. push_back(a, front(b));
  92. pop_front(b);
  93. }
  94. rev[a] ^= 1;
  95. } else
  96. {
  97. while (size(a))
  98. {
  99. push_front(b, back(a));
  100. pop_back(a);
  101. }
  102. rev[a] = rev[b] ^ 1;
  103. swap(q[a], q[b]);
  104. }
  105. }
  106. printf("%d", q[1].size());
  107. if (rev[1]) { while (q[1].size()) printf(" %d", q[1].back()), q[1].pop_back(); }
  108. else { while (q[1].size()) printf(" %d", q[1].front()), q[1].pop_front(); }
  109. puts("");
  110. }
  111. return 0;
  112. }

HNCPC2019部分题解的更多相关文章

  1. 2019年湖南省大学生计算机程序设计竞赛 (HNCPC2019) 简要题解

    2019年湖南省大学生计算机程序设计竞赛 (HNCPC2019) 简要题解 update10.01 突然发现叉姐把这场的题传到牛客上了,现在大家可以有地方提交了呢. 不知道该干什么所以就来水一篇题解 ...

  2. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  3. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  4. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  5. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  6. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  7. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  8. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  9. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

随机推荐

  1. 超详细的DOM操作(增删改查)

    操作DOM的核心就是增删改查 原文地址:https://jianshu.com/p/b0aa846f4dcc 目录 一.节点创建型API 1.1 createElement 1.2 createTex ...

  2. 在Eclipse-jee-neon中配置Hibernate(jbosstools)

    以下是在eclipse上安装Hibernate的插件,hibernate在编程上坚持出现,故需要在eclipse上进行如下安装. 首先把安装Hibernate插件,常用到JBoss,访问http:// ...

  3. Jupyter Notebook 安装与使用

    Ref: https://jupyter.org/install Installing Jupyter Notebook with pip python -m pip install --upgrad ...

  4. SQL中exists和in的区别

  5. generator (1)

    function array () { console.log(arguments) }; array(1,2,3) 从这里我们可以看出 数组的迭代方法里面有一个 属性  [Symbol.iterat ...

  6. BitMap位图

    BitMap位图算法https://blog.csdn.net/varyall/article/details/79662029 常见面试题 题1:在2.5亿个整数找出不重复的整数,内存不足以容纳着2 ...

  7. CH5E26 扑克牌 (计数类DP)

    $ CH~5E26~\times ~ $ 扑克牌: (计数类DP) $ solution: $ 唉,计数类DP总是这么有套路,就是想不到. 这道题我们首先可以发现牌的花色没有价值,只需要知道每种牌有 ...

  8. JS基础入门篇( 三 )—使用JS获取页面中某个元素的4种方法以及之间的差别( 一 )

    1.使用JS获取页面中某个元素的4种方法 1.通过id名获取元素 document.getElementById("id名"); 2.通过class名获取元素 document.g ...

  9. springboot-启动一段时间图片不能上传

    问题:[B2B]后台服务.PC服务.APP服务.仓储服务,启动一段时间图片不能上传. 原因:/tmp下以tomcat开头的目录被清理了. 处理方案:1.找到涉及服务器 注:后台服务.PC服务.APP服 ...

  10. 【leetcode】1033. Moving Stones Until Consecutive

    题目如下: Three stones are on a number line at positions a, b, and c. Each turn, you pick up a stone at ...