日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HAOI2017 简要题解

發布時間:2023/12/16 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HAOI2017 简要题解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

「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)\) 的,瓶頸在網絡流上。

代碼

注意一開始給的是無向邊,不能直接二分圖上連邊,先二分圖染色后,左邊向右邊連邊。

#include <bits/stdc++.h>#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endlusing namespace std;template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }inline int read() {int x(0), sgn(1); char ch(getchar());for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);return x * sgn; }void File() { #ifdef zjp_shadowfreopen ("2276.in", "r", stdin);freopen ("2276.out", "w", stdout); #endif }const int N = 1e4 + 1e3, M = 1.5e5 + 1e3, inf = 0x3f3f3f3f;template<int Maxn, int Maxm> struct Dinic {int Head[Maxn], Next[Maxm], to[Maxm], cap[Maxm], e;Dinic() { e = 1; }inline void add_edge(int u, int v, int w) {to[++ e] = v; cap[e] = w; Next[e] = Head[u]; Head[u] = e;}inline void Add(int u, int v, int w) {add_edge(u, v, w); add_edge(v, u, 0);}int dis[Maxn], S, T;bool Bfs() {queue<int> Q;Set(dis, 0); dis[S] = 1; Q.push(S);while (!Q.empty()) {int u = Q.front(); Q.pop();for (int i = Head[u], v = to[i]; i; v = to[i = Next[i]])if (cap[i] && !dis[v]) dis[v] = dis[u] + 1, Q.push(v);}return dis[T];}int cur[Maxn];int Dfs(int u, int flow) {if (u == T || !flow) return flow;int res = 0, f;for (int &i = cur[u], v = to[i]; i; v = to[i = Next[i]]) if (dis[v] == dis[u] + 1 && (f = Dfs(v, min(flow, cap[i])))) {cap[i] -= f; cap[i ^ 1] += f; res += f;if (!(flow -= f)) break;}return res;}int Run() {int res = 0;while (Bfs())Cpy(cur, Head), res += Dfs(S, inf);return res;}};Dinic<N, M << 1> T;int n, m; vector<int> G[N]; map<int, bool> Map[N];void Build() {For (u, 1, T.T)for (int i = T.Head[u], v = T.to[i]; i; v = T.to[i = T.Next[i]]) if (T.cap[i]) G[u].push_back(v), Map[v][u] = true; }int lowlink[N], dfn[N], sccno[N], stk[N], top, scc_cnt;void Tarjan(int u) {static int clk = 0;lowlink[u] = dfn[stk[++ top] = u] = ++ clk;for (int v : G[u]) if (!dfn[v])Tarjan(v), chkmin(lowlink[u], lowlink[v]);else if (!sccno[v]) chkmin(lowlink[u], dfn[v]);if (lowlink[u] == dfn[u]) {++ scc_cnt; int cur;do sccno[cur = stk[top --]] = scc_cnt; while (cur != u);} }int u[M], v[M];vector<pair<int, int>> ans;vector<int> E[N];int col[N]; void Color(int u) {for (int v : E[u]) if (!col[v])col[v] = col[u] ^ 3, Color(v); }int main () {File();n = read(); m = read();T.T = (T.S = n + 1) + 1;For (i, 1, m) {u[i] = read(), v[i] = read(); E[u[i]].push_back(v[i]);E[v[i]].push_back(u[i]);}For (i, 1, n) if (!col[i]) col[i] = 1, Color(i);For (i, 1, n)if (col[i] == 1) T.Add(T.S, i, 1); else T.Add(i, T.T, 1);For (i, 1, m) {if (col[u[i]] == 2) swap(u[i], v[i]);T.Add(u[i], v[i], 1);}T.Run(); Build();For (i, 1, T.T) if (!dfn[i]) Tarjan(i);For (i, 1, m)if (sccno[u[i]] != sccno[v[i]] && Map[u[i]][v[i]]) {if (u[i] > v[i]) swap(u[i], v[i]); ans.emplace_back(u[i], v[i]);}sort(ans.begin(), ans.end());printf ("%d\n", int(ans.size()));for (auto it : ans)printf ("%d %d\n", it.first, it.second);return 0;}

「HAOI2017」方案數

題意

考慮定義非負整數間的 “$ \subseteq $” ,如果 $ a \subseteq b $,那么 $ a \land b = a $,其中 $ \land $ 表示二進制下的“與”操作。

考慮現在有一個無限大的空間,現在你在 \((0, 0, 0)\),有三種位移操作。

  • $ (x, y, z) \to (ax, y, z) $ 當且僅當 $ x \subseteq ax $;
  • $ (x, y, z) \to (x, ay, z) $ 當且僅當 $ y \subseteq ay $;
  • $ (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
    如果是啥高維偏序就沒啥意思了。。

    代碼

    #include <bits/stdc++.h>#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl #define bit(x) __builtin_popcountll(x)using namespace std;using ll = long long;template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }template<typename T = ll> inline ll read() {ll x(0), sgn(1); char ch(getchar());for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);return x * sgn; }void File() { #ifdef zjp_shadowfreopen ("2277.in", "r", stdin);freopen ("2277.out", "w", stdout); #endif }const int N = 1e4 + 1e3, LN = 80, Mod = 998244353;ll n, m, r;struct Node {ll x, y, z; } P[N];struct Cmp {inline bool operator () (const Node &lhs, const Node &rhs) const {if (lhs.x != rhs.x) return lhs.x < rhs.x;if (lhs.y != rhs.y) return lhs.y < rhs.y;return lhs.z < rhs.z;} };int f[N], g[LN][LN][LN], comb[LN][LN];int main () {File();n = read(); m = read(); r = read();int lim = ceil(log2(max({n, m, r})) + 1);For (i, 0, lim) {comb[i][0] = 1;For (j, 1, i) comb[i][j] = (comb[i - 1][j] + comb[i - 1][j - 1]) % Mod;}g[0][0][0] = 1;For (x, 0, lim) For (y, 0, lim) For (z, 0, lim) {For (ax, 0, x - 1) g[x][y][z] = (g[x][y][z] + 1ll * g[ax][y][z] * comb[x][x - ax]) % Mod;For (ay, 0, y - 1) g[x][y][z] = (g[x][y][z] + 1ll * g[x][ay][z] * comb[y][y - ay]) % Mod;For (az, 0, z - 1)g[x][y][z] = (g[x][y][z] + 1ll * g[x][y][az] * comb[z][z - az]) % Mod;}int o = read<int>();For (i, 1, o) {ll x = read(), y = read(), z = read();P[i] = (Node) {x, y, z};}P[++ o] = (Node) {n, m, r};sort(P + 1, P + o + 1, Cmp());For (i, 1, o) {f[i] = g[bit(P[i].x)][bit(P[i].y)][bit(P[i].z)];For (j, 1, i - 1)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)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;}f[o] = (f[o] + Mod) % Mod;printf ("%d\n", f[o]);return 0;}

    「HAOI2017」字符串

    題意

    給出一個字符串 $ s $ 和 $ n $ 個字符串 $ p_i $,求每個字符串 $ p_i $ 在 $ s $ 中出現的次數。注意這里兩個字符串相等的定義稍作改變。

    給定一個常數 $ k $,對于兩個字符串 $ a, b $,如果 $ a = b $,那么滿足:

  • $ |a| = |b| $;
  • 對于所有 $ 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|))\) 的啦。

    代碼

    #include <bits/stdc++.h>#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl #define epb emplace_back #define fir first #define sec secondusing namespace std;using PII = pair<int, int>;template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }inline int read() {int x(0), sgn(1); char ch(getchar());for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);return x * sgn; }void File() { #ifdef zjp_shadowfreopen ("2278.in", "r", stdin);freopen ("2278.out", "w", stdout); #endif }const int N = 2e5 + 1e3;vector<int> G[N << 1];template<int Maxn, int Alpha> struct Aho_Corasick_Automaton {int ch[Maxn][Alpha], fail[Maxn], Node;Aho_Corasick_Automaton() { Node = 1; }inline int Insert(int pos, int c) {if (!ch[pos][c]) ch[pos][c] = ++ Node; return ch[pos][c];}void Get_Fail() {queue<int> Q; Rep (i, Alpha) {if (ch[1][i]) Q.push(ch[1][i]), fail[ch[1][i]] = 1;else ch[1][i] = 1;}while (!Q.empty()) {int u = Q.front(); Q.pop();Rep (i, Alpha) {int &v = ch[u][i];if (v) fail[v] = ch[fail[u]][i], Q.push(v);else v = ch[fail[u]][i];}}}void Get_Tree() {For (i, 2, Node) G[fail[i]].epb(i);}};int clk;template<int Maxn> struct Fenwick_Tree {#define lowbit(x) (x & -x)int sumv[Maxn];inline void Update(int pos) {for (; pos <= clk; pos += lowbit(pos)) ++ sumv[pos];}inline int Query(int pos) {int res = 0;for (; pos; pos -= lowbit(pos)) res += sumv[pos];return res;}};Aho_Corasick_Automaton<N << 1, 94> ACAM;Fenwick_Tree<N << 1> FT[2];int dfn[N << 1], efn[N << 1];void Dfs_Init(int u = 1) {dfn[u] = ++ clk; for (int v : G[u]) Dfs_Init(v); efn[u] = clk; }inline int Ask(int opt, int pos) {return FT[opt].Query(efn[pos]) - FT[opt].Query(dfn[pos] - 1); }int ans[N]; vector<PII> Q[N << 1]; vector<int> V[N << 1];void Process(int u) {for (PII cur : Q[u])ans[cur.fir] += (cur.sec > 0 ? -1 : 1) * Ask(cur.sec < 0, abs(cur.sec));for (int cur : V[u])FT[cur < 0].Update(dfn[abs(cur)]);for (int v : G[u]) Process(v);for (PII cur : Q[u])ans[cur.fir] += (cur.sec > 0 ? 1 : -1) * Ask(cur.sec < 0, abs(cur.sec)); }int n, k; char S[N], T[N];int L[N], R[N], pos[2][N];int main () {File();k = read(); scanf ("%s", S + 1); int lenS = strlen(S + 1);n = read();For (i, 1, n) {scanf ("%s", T + 1);int lenT = strlen(T + 1), u = 1;if (lenT < k) {ans[i] = lenS - lenT + 1; continue;}For (j, 1, lenT) L[j] = u = ACAM.Insert(u, T[j] - 33); u = 1;Fordown (j, lenT, 1) R[j] = u = ACAM.Insert(u, T[j] - 33);For (j, 0, lenT - k) Q[j ? L[j] : 1].epb(i, j == jend ? 1 : R[j + k + 1]);For (j, 1, lenT - k) Q[L[j]].epb(i, - R[j + k]);}ACAM.Get_Fail(); ACAM.Get_Tree(); Dfs_Init();pos[0][0] = pos[1][lenS + 1] = 1;For (i, 1, lenS)pos[0][i] = ACAM.ch[pos[0][i - 1]][S[i] - 33];Fordown (i, lenS, 1)pos[1][i] = ACAM.ch[pos[1][i + 1]][S[i] - 33];For (i, 0, lenS - k)V[pos[0][i]].epb(pos[1][i + k + 1]);For (i, 1, lenS - k)V[pos[0][i]].epb(- pos[1][i + k]);Process(1);For (i, 1, n) printf ("%d\n", ans[i]);return 0;}

    「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)))\) 的,可以跑過。

    代碼

    #include <bits/stdc++.h>#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__)using namespace std;template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;} template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;}inline int read() {int x(0), sgn(1); char ch(getchar());for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);return x * sgn; }void File() { #ifdef zjp_shadowfreopen ("2312.in", "r", stdin);freopen ("2312.out", "w", stdout); #endif }const int N = 1005, M = N << 1;typedef bitset<N> Info;struct Base {Info B[N];inline void Insert(Info cur) {Fordown (i, N - 5, 0)if (cur[i]) {if (!B[i].any()) { B[i] = cur; break; }else cur ^= B[i];}}Info Query() {Info res; res.reset();Fordown (i, N - 5, 0)if (!res[i]) res ^= B[i];return res;}};char str[N]; Info Trans() {Info res; res.reset();scanf ("%s", str);int len = strlen(str);reverse(str, str + len);For (i, 0, len)res[i] = str[i] == '1';return res; }inline void Out(Info cur) {bool flag = false;Fordown (i, N - 5, 0) {if (cur[i] == 1) flag = true;if (flag) putchar (cur[i] + 48);}putchar ('\n'); }int n, m, q;int fa[N]; int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }int Head[N], Next[M], to[M], e = 0; Info val[M]; inline void add_edge(int u, int v, Info w) {to[++ e] = v; Next[e] = Head[u]; Head[u] = e; val[e] = w; }Info dis[N]; void Dfs_Init(int u = 1, int fa = 0) {for (int i = Head[u]; i; i = Next[i]) {int v = to[i]; if (v == fa) continue ;dis[v] = dis[u] ^ val[i];Dfs_Init(v, u);} }int tim[N], now = 0;struct Option {int x, y; Info z;} lt[N];#define lson o << 1, l, mid #define rson o << 1 | 1, mid + 1, rInfo ans[N];template<int Maxn> struct Segment_Tree {vector<Option> V[Maxn];void Update(int o, int l, int r, int ul, int ur, Option uv) {if (ul <= l && r <= ur) { V[o].push_back(uv); return ; }int mid = (l + r) >> 1;if (ul <= mid) Update(lson, ul, ur, uv);if (ur > mid) Update(rson, ul, ur, uv);}void Dfs(int o, int l, int r, Base cur) {For (i, 0, V[o].size() - 1) {int u = V[o][i].x, v = V[o][i].y; Info w = V[o][i].z;cur.Insert(dis[u] ^ dis[v] ^ w);}if (l == r) { ans[l] = cur.Query(); return ; }int mid = (l + r) >> 1;Dfs(lson, cur); Dfs(rson, cur);}};Segment_Tree<N << 2> T;bool Cancel[N];int main () {File();n = read(); m = read(); q = read(); For (i, 1, n) fa[i] = i;For (i, 1, m) {int u = read(), v = read(); Info w = Trans();if (find(u) != find(v))fa[find(u)] = find(v), add_edge(u, v, w), add_edge(v, u, w);elseT.Update(1, 1, q + 1, 1, q + 1, (Option){u, v, w});}Dfs_Init();For (i, 1, q) {scanf ("%s", str + 1);if (str[1] == 'A') {int u = read(), v = read(); Info w = Trans();lt[++ now] = (Option){u, v, w}, tim[now] = i + 1;} else if (str[2] == 'a') {int id = read(); Cancel[id] = true;T.Update(1, 1, q + 1, tim[id], i, lt[id]);} else {int id = read(); Info w = Trans();T.Update(1, 1, q + 1, tim[id], i, lt[id]); tim[id] = i + 1; lt[id].z = w; }}For (i, 1, q) if (!Cancel[i])T.Update(1, 1, q + 1, tim[i], q + 1, lt[i]);T.Dfs(1, 1, q + 1, Base());For (i, 1, q + 1) Out(ans[i]);return 0;}

    「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)\)

    代碼

    #include <bits/stdc++.h>#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endlusing namespace std;template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }inline int read() {int x(0), sgn(1); char ch(getchar());for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);return x * sgn; }void File() { #ifdef zjp_shadowfreopen ("2313.in", "r", stdin);freopen ("2313.out", "w", stdout); #endif }const int N = 1e5 + 1e3, L = 40;vector<int> V[N]; int lef[L][N];namespace Trie {const int Maxn = N * L;int ch[Maxn][2], Node; vector<int> ver[Maxn];int Insert(int *P, int Len, int pos) {int u = 0;Rep (i, Len) {ver[u].push_back(pos);int &v = ch[u][P[i]];if (!v) v = ++ Node; u = v;}ver[u].push_back(pos);return u;}void Get(int u, int len) {if (int(ver[u].size()) <= 1) return;Rep (i, ver[u].size() - 1)lef[len][ver[u][i + 1]] = ver[u][i];Rep (id, 2) if (ch[u][id]) Get(ch[u][id], len + 1);}}int P[N], n, q, id[N]; char str[N]; struct Seg { int pos, val; } S[N];int main () {using namespace Trie;File();n = read(); q = read();scanf ("%s", str + 1);For (i, 1, n) P[i] = str[i] ^ '0';For (i, 1, n) id[i] = Insert(P + i, min(L - 1, n - i + 1), i);Get(0, 0);Rep (i, L) For (j, 1, n) chkmax(lef[i][j], lef[i][j - 1]);For (tim, 1, q) {int l = read(), r = read();Rep (i, L) S[i] = (Seg) {lef[i][r], i};sort(S, S + L, [&](Seg a, Seg b) { return a.pos != b.pos ? a.pos < b.pos : a.val > b.val; });int ans = 0, Last = l - 1;Rep (i, L) {if (S[i].pos < l) continue;if (i && S[i].pos == S[i - 1].pos) continue;ans += S[i].val * (S[i].pos - Last); Last = S[i].pos;}printf ("%d\n", ans);}return 0;}

    轉載于:https://www.cnblogs.com/zjp-shadow/p/10375149.html

    總結

    以上是生活随笔為你收集整理的HAOI2017 简要题解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。