「HAOI2017」新型城市化

题意

有一个 \(n\) 个点的无向图,其中只有 \(m\) 对点之间没有连边,保证这张图可以被分为至多两个团。

对于 \(m\) 对未连边的点对,判断有哪些点对满足将他们连边后最大团的大小增加。

\(n \le 10^4 , m \le 1.5 × 10^5\)

题解

脑洞图论题我真的一道都不会。

考虑原图的话,有 \(n^2\) 条边,显然是不行的。可以考虑补图,那么只有它给出的 \(m\) 条边,那么这个图一定是个二分图。

因为题目保证了原图可以被至多两个团覆盖,也就是意味着剩下的 \(m\) 条边两个端点各属于两个团中的一个。

原图上的最大团 \(=\) 反图上的最大独立集 \(=\) 二分图的最大独立集 \(=\) 点数减去最大匹配数。

那么题目就是问去掉哪些边后最大匹配数减少,也就是哪些边一定在二分图最大匹配上。这题中 \(n, m\) 较大,需要用 \(Dinic\) 算二分图匹配。接下来就只需要判断哪些边在最大匹配上啦。

显然它们一定要满流,其次边上的两个点在残量网络上不能在同一个强连通分量中。

因为如果他们在同一个环中,就可以将环上未匹配的边设为匹配边,匹配边设为未匹配边,最大匹配显然不变。

最后复杂度是 \(\mathcal O(m \sqrt n)\) 的,瓶颈在网络流上。

代码

注意一开始给的是无向边,不能直接二分图上连边,先二分图染色后,左边向右边连边。

  1. #include <bits/stdc++.h>
  2. #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
  3. #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
  4. #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
  5. #define Set(a, v) memset(a, v, sizeof(a))
  6. #define Cpy(a, b) memcpy(a, b, sizeof(a))
  7. #define debug(x) cout << #x << ": " << (x) << endl
  8. using namespace std;
  9. template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
  10. template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
  11. inline int read() {
  12. int x(0), sgn(1); char ch(getchar());
  13. for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
  14. for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
  15. return x * sgn;
  16. }
  17. void File() {
  18. #ifdef zjp_shadow
  19. freopen ("2276.in", "r", stdin);
  20. freopen ("2276.out", "w", stdout);
  21. #endif
  22. }
  23. const int N = 1e4 + 1e3, M = 1.5e5 + 1e3, inf = 0x3f3f3f3f;
  24. template<int Maxn, int Maxm>
  25. struct Dinic {
  26. int Head[Maxn], Next[Maxm], to[Maxm], cap[Maxm], e;
  27. Dinic() { e = 1; }
  28. inline void add_edge(int u, int v, int w) {
  29. to[++ e] = v; cap[e] = w; Next[e] = Head[u]; Head[u] = e;
  30. }
  31. inline void Add(int u, int v, int w) {
  32. add_edge(u, v, w); add_edge(v, u, 0);
  33. }
  34. int dis[Maxn], S, T;
  35. bool Bfs() {
  36. queue<int> Q;
  37. Set(dis, 0); dis[S] = 1; Q.push(S);
  38. while (!Q.empty()) {
  39. int u = Q.front(); Q.pop();
  40. for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]])
  41. if (cap[i] && !dis[v]) dis[v] = dis[u] + 1, Q.push(v);
  42. }
  43. return dis[T];
  44. }
  45. int cur[Maxn];
  46. int Dfs(int u, int flow) {
  47. if (u == T || !flow) return flow;
  48. int res = 0, f;
  49. for (int &i = cur[u], v = to[i]; i; v = to[i = Next[i]])
  50. if (dis[v] == dis[u] + 1 && (f = Dfs(v, min(flow, cap[i])))) {
  51. cap[i] -= f; cap[i ^ 1] += f; res += f;
  52. if (!(flow -= f)) break;
  53. }
  54. return res;
  55. }
  56. int Run() {
  57. int res = 0;
  58. while (Bfs())
  59. Cpy(cur, Head), res += Dfs(S, inf);
  60. return res;
  61. }
  62. };
  63. Dinic<N, M << 1> T;
  64. int n, m; vector<int> G[N];
  65. map<int, bool> Map[N];
  66. void Build() {
  67. For (u, 1, T.T)
  68. for (int i = T.Head[u], v = T.to[i]; i; v = T.to[i = T.Next[i]])
  69. if (T.cap[i]) G[u].push_back(v), Map[v][u] = true;
  70. }
  71. int lowlink[N], dfn[N], sccno[N], stk[N], top, scc_cnt;
  72. void Tarjan(int u) {
  73. static int clk = 0;
  74. lowlink[u] = dfn[stk[++ top] = u] = ++ clk;
  75. for (int v : G[u]) if (!dfn[v])
  76. Tarjan(v), chkmin(lowlink[u], lowlink[v]);
  77. else if (!sccno[v]) chkmin(lowlink[u], dfn[v]);
  78. if (lowlink[u] == dfn[u]) {
  79. ++ scc_cnt; int cur;
  80. do sccno[cur = stk[top --]] = scc_cnt; while (cur != u);
  81. }
  82. }
  83. int u[M], v[M];
  84. vector<pair<int, int>> ans;
  85. vector<int> E[N];
  86. int col[N];
  87. void Color(int u) {
  88. for (int v : E[u]) if (!col[v])
  89. col[v] = col[u] ^ 3, Color(v);
  90. }
  91. int main () {
  92. File();
  93. n = read(); m = read();
  94. T.T = (T.S = n + 1) + 1;
  95. For (i, 1, m) {
  96. u[i] = read(), v[i] = read();
  97. E[u[i]].push_back(v[i]);
  98. E[v[i]].push_back(u[i]);
  99. }
  100. For (i, 1, n) if (!col[i]) col[i] = 1, Color(i);
  101. For (i, 1, n)
  102. if (col[i] == 1) T.Add(T.S, i, 1); else T.Add(i, T.T, 1);
  103. For (i, 1, m) {
  104. if (col[u[i]] == 2) swap(u[i], v[i]);
  105. T.Add(u[i], v[i], 1);
  106. }
  107. T.Run(); Build();
  108. For (i, 1, T.T) if (!dfn[i]) Tarjan(i);
  109. For (i, 1, m)
  110. if (sccno[u[i]] != sccno[v[i]] && Map[u[i]][v[i]]) {
  111. if (u[i] > v[i]) swap(u[i], v[i]); ans.emplace_back(u[i], v[i]);
  112. }
  113. sort(ans.begin(), ans.end());
  114. printf ("%d\n", int(ans.size()));
  115. for (auto it : ans)
  116. printf ("%d %d\n", it.first, it.second);
  117. return 0;
  118. }

「HAOI2017」方案数

题意

考虑定义非负整数间的 “$ \subseteq $” ,如果 $ a \subseteq b $,那么 $ a \land b = a $,其中 $ \land $ 表示二进制下的“与”操作。

考虑现在有一个无限大的空间,现在你在 \((0, 0, 0)\),有三种位移操作。

  1. $ (x, y, z) \to (ax, y, z) $ 当且仅当 $ x \subseteq ax $;
  2. $ (x, y, z) \to (x, ay, z) $ 当且仅当 $ y \subseteq ay $;
  3. $ (x, y, z) \to (x, y, az) $ 当且仅当 $ z \subseteq az $。

有 \(o\) 个点不能经过了。现在问你到某个点 \((n, m, r)\) 的方案数,答案对 \(998244353\) 取模。

\(n, m, r \le 10^{18}, o \le 10^4\)

题解

首先考虑没有障碍的时候怎么做,不难发现答案只与 \(n, m, r\) 二进制下 \(1\) 的个数有关。

为什么呢?考虑操作的实质,其实就是个 \(x, y, z\) 中其中一个数在二进制下的一些 \(0\) 变成 \(1\) 。

那么就令 \(g_{i, j, k}\) 为三维分别有 \(i, j, k\) 个 \(1\) 的方案数,这部分是 \(O(\log^4 \max\{n, m, r\})\) 的。

那么预处理这个后就比较好做了,此时变成一个经典容斥模型。

设 \(f_i\) 为第一次碰到的关键点为 \(i\) 个点的方案数,那么直接做 \(\mathcal O(o^2)\) 容斥即可。

这样常数其实挺小的,可以跑过。但不知道有什么更高妙的做法 QAQ

如果是啥高维偏序就没啥意思了。。

代码

  1. #include <bits/stdc++.h>
  2. #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
  3. #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
  4. #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
  5. #define Set(a, v) memset(a, v, sizeof(a))
  6. #define Cpy(a, b) memcpy(a, b, sizeof(a))
  7. #define debug(x) cout << #x << ": " << (x) << endl
  8. #define bit(x) __builtin_popcountll(x)
  9. using namespace std;
  10. using ll = long long;
  11. template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
  12. template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
  13. template<typename T = ll>
  14. inline ll read() {
  15. ll x(0), sgn(1); char ch(getchar());
  16. for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
  17. for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
  18. return x * sgn;
  19. }
  20. void File() {
  21. #ifdef zjp_shadow
  22. freopen ("2277.in", "r", stdin);
  23. freopen ("2277.out", "w", stdout);
  24. #endif
  25. }
  26. const int N = 1e4 + 1e3, LN = 80, Mod = 998244353;
  27. ll n, m, r;
  28. struct Node {
  29. ll x, y, z;
  30. } P[N];
  31. struct Cmp {
  32. inline bool operator () (const Node &lhs, const Node &rhs) const {
  33. if (lhs.x != rhs.x) return lhs.x < rhs.x;
  34. if (lhs.y != rhs.y) return lhs.y < rhs.y;
  35. return lhs.z < rhs.z;
  36. }
  37. };
  38. int f[N], g[LN][LN][LN], comb[LN][LN];
  39. int main () {
  40. File();
  41. n = read(); m = read(); r = read();
  42. int lim = ceil(log2(max({n, m, r})) + 1);
  43. For (i, 0, lim) {
  44. comb[i][0] = 1;
  45. For (j, 1, i) comb[i][j] = (comb[i - 1][j] + comb[i - 1][j - 1]) % Mod;
  46. }
  47. g[0][0][0] = 1;
  48. For (x, 0, lim) For (y, 0, lim) For (z, 0, lim) {
  49. For (ax, 0, x - 1)
  50. g[x][y][z] = (g[x][y][z] + 1ll * g[ax][y][z] * comb[x][x - ax]) % Mod;
  51. For (ay, 0, y - 1)
  52. g[x][y][z] = (g[x][y][z] + 1ll * g[x][ay][z] * comb[y][y - ay]) % Mod;
  53. For (az, 0, z - 1)
  54. g[x][y][z] = (g[x][y][z] + 1ll * g[x][y][az] * comb[z][z - az]) % Mod;
  55. }
  56. int o = read<int>();
  57. For (i, 1, o) {
  58. ll x = read(), y = read(), z = read();
  59. P[i] = (Node) {x, y, z};
  60. }
  61. P[++ o] = (Node) {n, m, r};
  62. sort(P + 1, P + o + 1, Cmp());
  63. For (i, 1, o) {
  64. f[i] = g[bit(P[i].x)][bit(P[i].y)][bit(P[i].z)];
  65. For (j, 1, i - 1)
  66. if ((P[j].x & P[i].x) == P[j].x && (P[j].y & P[i].y) == P[j].y && (P[j].z & P[i].z) == P[j].z)
  67. f[i] = (f[i] - 1ll * f[j] * g[bit(P[i].x ^ P[j].x)][bit(P[i].y ^ P[j].y)][bit(P[i].z ^ P[j].z)]) % Mod;
  68. }
  69. f[o] = (f[o] + Mod) % Mod;
  70. printf ("%d\n", f[o]);
  71. return 0;
  72. }

「HAOI2017」字符串

题意

给出一个字符串 $ s $ 和 $ n $ 个字符串 $ p_i $,求每个字符串 $ p_i $ 在 $ s $ 中出现的次数。注意这里两个字符串相等的定义稍作改变。

给定一个常数 $ k $,对于两个字符串 $ a, b $,如果 $ a = b $,那么满足:

  1. $ |a| = |b| $;
  2. 对于所有 $ a_i \neq b_i $ 以及 $ a_j \neq b_j $,满足 $ |i-j| < k $。

特别地,如果 $ |a| = |b| \le k $,那么认为 $ a = b $。

$ |s|, \sum |p_i| \le 2 \cdot 10^5 $

题解

神仙题 QAQ 还是对字符串不太熟

考虑把所有 \(p_i\) 的正串和反串一起建一个 \(AC\) 自动机。

然后原串在上面跑,考虑一个自动机上一个节点 \(u\) 假设深度为 \(i\) ,如果他的下一位不匹配,那么我们只需要让 \(i + k + 1\) 之后的都匹配就可以了。

我们现在需要统计的就是对于这个节点 \(u\) 来说 \(s\) 有多少个位置 恰好 匹配了前 \(i\) 个位置,然后隔着 \(i + k + 1\) 后面都能匹配上。

其实就是所有满足 \(u\) 的 \(fail\) 树内 \(s\) 匹配到的位置 \(j\) 满足 \(j + k + 1\) 在 \(u\) 对应节点的字符串的反串的第 \(i + k + 1\) 位对应节点的 \(fail\) 树内的个数(注意此处所有提到位置都以正串为准)。

我们其实就是要统计这样一个东西,把每个字符串询问都挂在对应每一位的 \(AC\) 自动机上的节点。

\(s\) 匹配的位置产生贡献同样挂到 \(AC\) 自动机上的节点上。

然后显然是可以用线段树合并统计贡献的,但是没有必要。由于是加减,满足差分,那么我们进子树的时候减掉,出子树的时候加上就行了。

但是这样是会算重复的,记得前面我们提到的恰好吗?此处对于 \(i + k\) 也匹配上的方案也是会加上来的。

我们多挂个询问把重复算的贡献减掉就行啦。

最后复杂度是 \(\mathcal O((|s| + \sum |p|)\log (\sum |p|))\) 的啦。

代码


  1. #include <bits/stdc++.h>
  2. #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
  3. #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
  4. #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
  5. #define Set(a, v) memset(a, v, sizeof(a))
  6. #define Cpy(a, b) memcpy(a, b, sizeof(a))
  7. #define debug(x) cout << #x << ": " << (x) << endl
  8. #define epb emplace_back
  9. #define fir first
  10. #define sec second
  11. using namespace std;
  12. using PII = pair<int, int>;
  13. template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
  14. template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
  15. inline int read() {
  16. int x(0), sgn(1); char ch(getchar());
  17. for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
  18. for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
  19. return x * sgn;
  20. }
  21. void File() {
  22. #ifdef zjp_shadow
  23. freopen ("2278.in", "r", stdin);
  24. freopen ("2278.out", "w", stdout);
  25. #endif
  26. }
  27. const int N = 2e5 + 1e3;
  28. vector<int> G[N << 1];
  29. template<int Maxn, int Alpha>
  30. struct Aho_Corasick_Automaton {
  31. int ch[Maxn][Alpha], fail[Maxn], Node;
  32. Aho_Corasick_Automaton() { Node = 1; }
  33. inline int Insert(int pos, int c) {
  34. if (!ch[pos][c]) ch[pos][c] = ++ Node;
  35. return ch[pos][c];
  36. }
  37. void Get_Fail() {
  38. queue<int> Q;
  39. Rep (i, Alpha) {
  40. if (ch[1][i])
  41. Q.push(ch[1][i]), fail[ch[1][i]] = 1;
  42. else ch[1][i] = 1;
  43. }
  44. while (!Q.empty()) {
  45. int u = Q.front(); Q.pop();
  46. Rep (i, Alpha) {
  47. int &v = ch[u][i];
  48. if (v) fail[v] = ch[fail[u]][i], Q.push(v);
  49. else v = ch[fail[u]][i];
  50. }
  51. }
  52. }
  53. void Get_Tree() {
  54. For (i, 2, Node) G[fail[i]].epb(i);
  55. }
  56. };
  57. int clk;
  58. template<int Maxn>
  59. struct Fenwick_Tree {
  60. #define lowbit(x) (x & -x)
  61. int sumv[Maxn];
  62. inline void Update(int pos) {
  63. for (; pos <= clk; pos += lowbit(pos))
  64. ++ sumv[pos];
  65. }
  66. inline int Query(int pos) {
  67. int res = 0;
  68. for (; pos; pos -= lowbit(pos))
  69. res += sumv[pos];
  70. return res;
  71. }
  72. };
  73. Aho_Corasick_Automaton<N << 1, 94> ACAM;
  74. Fenwick_Tree<N << 1> FT[2];
  75. int dfn[N << 1], efn[N << 1];
  76. void Dfs_Init(int u = 1) {
  77. dfn[u] = ++ clk; for (int v : G[u]) Dfs_Init(v); efn[u] = clk;
  78. }
  79. inline int Ask(int opt, int pos) {
  80. return FT[opt].Query(efn[pos]) - FT[opt].Query(dfn[pos] - 1);
  81. }
  82. int ans[N]; vector<PII> Q[N << 1]; vector<int> V[N << 1];
  83. void Process(int u) {
  84. for (PII cur : Q[u])
  85. ans[cur.fir] += (cur.sec > 0 ? -1 : 1) * Ask(cur.sec < 0, abs(cur.sec));
  86. for (int cur : V[u])
  87. FT[cur < 0].Update(dfn[abs(cur)]);
  88. for (int v : G[u]) Process(v);
  89. for (PII cur : Q[u])
  90. ans[cur.fir] += (cur.sec > 0 ? 1 : -1) * Ask(cur.sec < 0, abs(cur.sec));
  91. }
  92. int n, k; char S[N], T[N];
  93. int L[N], R[N], pos[2][N];
  94. int main () {
  95. File();
  96. k = read(); scanf ("%s", S + 1);
  97. int lenS = strlen(S + 1);
  98. n = read();
  99. For (i, 1, n) {
  100. scanf ("%s", T + 1);
  101. int lenT = strlen(T + 1), u = 1;
  102. if (lenT < k) {
  103. ans[i] = lenS - lenT + 1; continue;
  104. }
  105. For (j, 1, lenT) L[j] = u = ACAM.Insert(u, T[j] - 33); u = 1;
  106. Fordown (j, lenT, 1) R[j] = u = ACAM.Insert(u, T[j] - 33);
  107. For (j, 0, lenT - k) Q[j ? L[j] : 1].epb(i, j == jend ? 1 : R[j + k + 1]);
  108. For (j, 1, lenT - k) Q[L[j]].epb(i, - R[j + k]);
  109. }
  110. ACAM.Get_Fail(); ACAM.Get_Tree(); Dfs_Init();
  111. pos[0][0] = pos[1][lenS + 1] = 1;
  112. For (i, 1, lenS)
  113. pos[0][i] = ACAM.ch[pos[0][i - 1]][S[i] - 33];
  114. Fordown (i, lenS, 1)
  115. pos[1][i] = ACAM.ch[pos[1][i + 1]][S[i] - 33];
  116. For (i, 0, lenS - k)
  117. V[pos[0][i]].epb(pos[1][i + k + 1]);
  118. For (i, 1, lenS - k)
  119. V[pos[0][i]].epb(- pos[1][i + k]);
  120. Process(1);
  121. For (i, 1, n) printf ("%d\n", ans[i]);
  122. return 0;
  123. }

「HAOI2017」八纵八横

题意

一开始有个 \(n\) 个点 \(m\) 条边的连通图,有 \(P\) 次操作。支持动态加边,删边(只会删加进去的边),修改边的权值。

每次操作后询问从 \(1\) 号点出发在 \(1\) 号点结束的最大异或和路径。不强制在线。

\(n \le 500, m \le 500, Q \le 1000, len \le 1000\)

\(len\) 为边权的二进制位长度。

题解

如果没有修改怎么做呢?知道一个结论就好啦。

任意一条 \(1\) 到 \(n\) 的路径的异或和,都可以由任意一条 \(1\) 到 \(n\) 路径的异或和与图中的一些环的异或和来组合得到。

为什么?

如果我们走一条路径的话,如果路径上存在一个环,那么这个环的总异或值就可以下放到线性基。因为把这个环走两遍就等于没走这个环,同样如果是由一条路径到的这个环,沿原路返回,那等于那条路径没走,只走了环。

在这种条件下,我们可以考虑把环储存为一个线性基的元素。因为这个元素是随意选不选的。

由于一开始的边是不会删除的,所以我们可以对一开始读入的边用并查集找环,然后搞出一棵原图的生成树,这样之后的插入就会构造出一个唯一的环,然后这个环的权值也可以方便地算出。

因为线性基不好撤销,我们考虑进行线段树分治。这样可以直接把线性基存在分治结构里,最多只会同时存在 \(O(\log)\) 个。

我们先记录好每条环边的存在区间,一开始的环边直接就是 \([0,q]\) 。注意每次对环边进行权值修改时,我们也要划分成两个存在区间。

做成这样后直接把这些边插到线段树里,然后直接线段树分治就可以了。

复杂度是 \(\displaystyle \mathcal O(n \alpha (n) + \frac{len^2}{\omega} (q \log q + (q + m -n)))\) 的,可以跑过。

代码

  1. #include <bits/stdc++.h>
  2. #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
  3. #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
  4. #define Set(a, v) memset(a, v, sizeof(a))
  5. #define Cpy(a, b) memcpy(a, b, sizeof(a))
  6. #define debug(x) cout << #x << ": " << (x) << endl
  7. #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
  8. using namespace std;
  9. template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
  10. template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;}
  11. inline int read() {
  12. int x(0), sgn(1); char ch(getchar());
  13. for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
  14. for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
  15. return x * sgn;
  16. }
  17. void File() {
  18. #ifdef zjp_shadow
  19. freopen ("2312.in", "r", stdin);
  20. freopen ("2312.out", "w", stdout);
  21. #endif
  22. }
  23. const int N = 1005, M = N << 1;
  24. typedef bitset<N> Info;
  25. struct Base {
  26. Info B[N];
  27. inline void Insert(Info cur) {
  28. Fordown (i, N - 5, 0)
  29. if (cur[i]) {
  30. if (!B[i].any()) { B[i] = cur; break; }
  31. else cur ^= B[i];
  32. }
  33. }
  34. Info Query() {
  35. Info res; res.reset();
  36. Fordown (i, N - 5, 0)
  37. if (!res[i]) res ^= B[i];
  38. return res;
  39. }
  40. };
  41. char str[N];
  42. Info Trans() {
  43. Info res; res.reset();
  44. scanf ("%s", str);
  45. int len = strlen(str);
  46. reverse(str, str + len);
  47. For (i, 0, len)
  48. res[i] = str[i] == '1';
  49. return res;
  50. }
  51. inline void Out(Info cur) {
  52. bool flag = false;
  53. Fordown (i, N - 5, 0) {
  54. if (cur[i] == 1) flag = true;
  55. if (flag) putchar (cur[i] + 48);
  56. }
  57. putchar ('\n');
  58. }
  59. int n, m, q;
  60. int fa[N];
  61. int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
  62. int Head[N], Next[M], to[M], e = 0; Info val[M];
  63. inline void add_edge(int u, int v, Info w) {
  64. to[++ e] = v; Next[e] = Head[u]; Head[u] = e; val[e] = w;
  65. }
  66. Info dis[N];
  67. void Dfs_Init(int u = 1, int fa = 0) {
  68. for (int i = Head[u]; i; i = Next[i]) {
  69. int v = to[i]; if (v == fa) continue ;
  70. dis[v] = dis[u] ^ val[i];
  71. Dfs_Init(v, u);
  72. }
  73. }
  74. int tim[N], now = 0;
  75. struct Option {
  76. int x, y; Info z;
  77. } lt[N];
  78. #define lson o << 1, l, mid
  79. #define rson o << 1 | 1, mid + 1, r
  80. Info ans[N];
  81. template<int Maxn>
  82. struct Segment_Tree {
  83. vector<Option> V[Maxn];
  84. void Update(int o, int l, int r, int ul, int ur, Option uv) {
  85. if (ul <= l && r <= ur) { V[o].push_back(uv); return ; }
  86. int mid = (l + r) >> 1;
  87. if (ul <= mid) Update(lson, ul, ur, uv);
  88. if (ur > mid) Update(rson, ul, ur, uv);
  89. }
  90. void Dfs(int o, int l, int r, Base cur) {
  91. For (i, 0, V[o].size() - 1) {
  92. int u = V[o][i].x, v = V[o][i].y; Info w = V[o][i].z;
  93. cur.Insert(dis[u] ^ dis[v] ^ w);
  94. }
  95. if (l == r) { ans[l] = cur.Query(); return ; }
  96. int mid = (l + r) >> 1;
  97. Dfs(lson, cur); Dfs(rson, cur);
  98. }
  99. };
  100. Segment_Tree<N << 2> T;
  101. bool Cancel[N];
  102. int main () {
  103. File();
  104. n = read(); m = read(); q = read();
  105. For (i, 1, n) fa[i] = i;
  106. For (i, 1, m) {
  107. int u = read(), v = read(); Info w = Trans();
  108. if (find(u) != find(v))
  109. fa[find(u)] = find(v), add_edge(u, v, w), add_edge(v, u, w);
  110. else
  111. T.Update(1, 1, q + 1, 1, q + 1, (Option){u, v, w});
  112. }
  113. Dfs_Init();
  114. For (i, 1, q) {
  115. scanf ("%s", str + 1);
  116. if (str[1] == 'A') {
  117. int u = read(), v = read(); Info w = Trans();
  118. lt[++ now] = (Option){u, v, w}, tim[now] = i + 1;
  119. } else if (str[2] == 'a') {
  120. int id = read(); Cancel[id] = true;
  121. T.Update(1, 1, q + 1, tim[id], i, lt[id]);
  122. } else {
  123. int id = read(); Info w = Trans();
  124. T.Update(1, 1, q + 1, tim[id], i, lt[id]); tim[id] = i + 1; lt[id].z = w;
  125. }
  126. }
  127. For (i, 1, q) if (!Cancel[i])
  128. T.Update(1, 1, q + 1, tim[i], q + 1, lt[i]);
  129. T.Dfs(1, 1, q + 1, Base());
  130. For (i, 1, q + 1) Out(ans[i]);
  131. return 0;
  132. }

「HAOI2017」供给侧改革

题意

一个长度为 $ n $ 的 $ 01 $ 字符串 \(S\) ,令 \(\operatorname{data}(l,r)\) 表示:在字符串 \(S\) 中,起始位置在 \([l,r]\) 之间的这些后缀之中,具有最长公共前缀的两个后缀的最长公共前缀的长度。

\(Q\) 次询问。对于每一个询问 \(L\),\(R\)。求

\[\mathit{ans} = \sum\limits_{ L \le i \lt R } \operatorname{data}(i, R)
\]

\(S\) 随机生成

$ n \leq 100000, Q \leq 100000 $

题解

首先是随机,答案长度肯定不会太大,我们设它为 \(L\) ,大概不超过 \(40\) 。

那么就有一个很显然的暴力了,把 \(n\) 个位置向后延伸的 \(L\) 个字符的串插入到 \(Trie\) 中。

每次从 \(R\) 向 \(L\) 扫,然后在树上把这个点到根的路径打标记,然后把当前的 ans 与之前打过标记且在这条路径上的最深点深度取 \(\max\) ,最后求和就是答案了。

复杂度是 \(\mathcal O(nQL)\) 的。

考虑优化,由于答案不超过 \(L\) ,且答案是单调不降的。我们可以考虑对于答案相同的一段连续算。

这个我们在 \(Trie\) 预处理出每一层对于每个 \(r\) 左边最靠右的满足条件的 \(l\) 即可。然后最后排次序,算贡献即可。

复杂度优化到了 \(\mathcal O((n + Q)L)\) 。

代码

  1. #include <bits/stdc++.h>
  2. #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
  3. #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
  4. #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
  5. #define Set(a, v) memset(a, v, sizeof(a))
  6. #define Cpy(a, b) memcpy(a, b, sizeof(a))
  7. #define debug(x) cout << #x << ": " << (x) << endl
  8. using namespace std;
  9. template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
  10. template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
  11. inline int read() {
  12. int x(0), sgn(1); char ch(getchar());
  13. for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
  14. for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
  15. return x * sgn;
  16. }
  17. void File() {
  18. #ifdef zjp_shadow
  19. freopen ("2313.in", "r", stdin);
  20. freopen ("2313.out", "w", stdout);
  21. #endif
  22. }
  23. const int N = 1e5 + 1e3, L = 40;
  24. vector<int> V[N]; int lef[L][N];
  25. namespace Trie {
  26. const int Maxn = N * L;
  27. int ch[Maxn][2], Node; vector<int> ver[Maxn];
  28. int Insert(int *P, int Len, int pos) {
  29. int u = 0;
  30. Rep (i, Len) {
  31. ver[u].push_back(pos);
  32. int &v = ch[u][P[i]];
  33. if (!v) v = ++ Node; u = v;
  34. }
  35. ver[u].push_back(pos);
  36. return u;
  37. }
  38. void Get(int u, int len) {
  39. if (int(ver[u].size()) <= 1) return;
  40. Rep (i, ver[u].size() - 1)
  41. lef[len][ver[u][i + 1]] = ver[u][i];
  42. Rep (id, 2) if (ch[u][id]) Get(ch[u][id], len + 1);
  43. }
  44. }
  45. int P[N], n, q, id[N]; char str[N];
  46. struct Seg { int pos, val; } S[N];
  47. int main () {
  48. using namespace Trie;
  49. File();
  50. n = read(); q = read();
  51. scanf ("%s", str + 1);
  52. For (i, 1, n) P[i] = str[i] ^ '0';
  53. For (i, 1, n) id[i] = Insert(P + i, min(L - 1, n - i + 1), i);
  54. Get(0, 0);
  55. Rep (i, L) For (j, 1, n) chkmax(lef[i][j], lef[i][j - 1]);
  56. For (tim, 1, q) {
  57. int l = read(), r = read();
  58. Rep (i, L)
  59. S[i] = (Seg) {lef[i][r], i};
  60. sort(S, S + L, [&](Seg a, Seg b) { return a.pos != b.pos ? a.pos < b.pos : a.val > b.val; });
  61. int ans = 0, Last = l - 1;
  62. Rep (i, L) {
  63. if (S[i].pos < l) continue;
  64. if (i && S[i].pos == S[i - 1].pos) continue;
  65. ans += S[i].val * (S[i].pos - Last); Last = S[i].pos;
  66. }
  67. printf ("%d\n", ans);
  68. }
  69. return 0;
  70. }

HAOI2017 简要题解的更多相关文章

  1. Noip 2014酱油记+简要题解

    好吧,day2T1把d默认为1也是醉了,现在只能期待数据弱然后怒卡一等线吧QAQ Day0 第一次下午出发啊真是不错,才2小时左右就到了233,在车上把sao和fate补掉就到了= = 然后到宾馆之后 ...

  2. Tsinghua 2018 DSA PA2简要题解

    反正没时间写,先把简要题解(嘴巴A题)都给他写了记录一下. upd:任务倒是完成了,我也自闭了. CST2018 2-1 Meteorites: 乘法版的石子合并,堆 + 高精度. 写起来有点烦貌似. ...

  3. Codeforces 863 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 简要题解?因为最后一题太毒不想写了所以其实是部分题解... A题 传送门 题意简述:给你一个数,问你能不能通过加前导000使其成为一个回文数 ...

  4. HNOI2018简要题解

    HNOI2018简要题解 D1T1 寻宝游戏 题意 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为 ...

  5. JXOI2018简要题解

    JXOI2018简要题解 T1 排序问题 题意 九条可怜是一个热爱思考的女孩子. 九条可怜最近正在研究各种排序的性质,她发现了一种很有趣的排序方法: Gobo sort ! Gobo sort 的算法 ...

  6. BJOI2018简要题解

    BJOI2018简要题解 D1T1 二进制 题意 pupil 发现对于一个十进制数,无论怎么将其的数字重新排列,均不影响其是不是 \(3\) 的倍数.他想研究对于二进制,是否也有类似的性质. 于是他生 ...

  7. CQOI2018简要题解

    CQOI2018简要题解 D1T1 破解 D-H 协议 题意 Diffie-Hellman 密钥交换协议是一种简单有效的密钥交换方法.它可以让通讯双方在没有事先约定密钥(密码)的情况下,通过不安全的信 ...

  8. AtCoder ExaWizards 2019 简要题解

    AtCoder ExaWizards 2019 简要题解 Tags:题解 link:https://atcoder.jp/contests/exawizards2019 很水的一场ARC啊,随随便便就 ...

  9. Comet OJ - Contest #2 简要题解

    Comet OJ - Contest #2 简要题解 cometoj A 模拟,复杂度是对数级的. code B 易知\(p\in[l,r]\),且最终的利润关于\(p\)的表达式为\(\frac{( ...

随机推荐

  1. echarts x轴 增加滚动条

    charts x轴 增加滚动条 在option 配置项中添加 [ dataZoom 中配置 ] 设置x轴滚动条 效果图: 动态拖动 以下参考代码 dataZoom配置 官网写法 option = { ...

  2. sso单点登录系统(解决session共享)

    场景:假设一个用户将自己的登录信息提交到后台,如果session保存的信息分布在多台机器上,并且不共享,那么可能导致用户的登录信息出现短暂的丢失,为什么这样讲,因为用户访问服务器中间还要经过负载均衡服 ...

  3. java.lang.LinkageError: JAXB 2.0 API is being loaded from the bootstrap classloader

    我的解决办法:    1.如果是application工程,则在程序中打印出         system.out.println(System.getProperty("java.endo ...

  4. Angular 基本指令

    <!DOCTYPE html><html ng-app><head lang="en"> <meta charset="UTF- ...

  5. Unit 2.前端之html--table(表格),form(表单)标签

    一.table标签 作用:定义html表格.一个table标签元素至少包含 thead(表头),tbody(表主题),还可以有tfoot(表底部) html表格游table元素及一个或者多个tr,th ...

  6. mvc后台可以使前台弹出警告框的几种方法

    1,引用博主的 第一种方案:在jsp页面使用java代码实现. 后台处理代码:(把要提示的数据放到session中.) String message = ""; message = ...

  7. 在linux系统中实现各项监控的关键技术(2)--内核态与用户态进程之间的通信netlink

    Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 ...

  8. python学习笔记(8)--random库的使用

    伪随机数:采用梅森旋转算法生成的伪随机序列中元素 使用random库 一.基本随机函数 随机数需要一个种子,依据这个种子通过梅森旋转算法产生固定序列的随机数.seed(a=None)  初始化给定的随 ...

  9. Spring Boot基础:Spring Boot简介与快速搭建(1)

    1. Spring Boot简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的创建.运行.调试.部署等. Spring Boot默认使用tomca ...

  10. 微信小程序flex佈局

    聲明:display:flex 換行flex-wrap:flex-wrap:nowrap(不換行).wrap(換行).wrap-reserve(第一行在下面): 主軸對齊(橫向對齊)justify-c ...