[CF587F]Duff is Mad[AC自动机+根号分治+分块]
生活随笔
收集整理的這篇文章主要介紹了
[CF587F]Duff is Mad[AC自动机+根号分治+分块]
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
題意
給你 \(n\) 個串 \(s_{1\cdots n}\) ,每次詢問給出 \(l,r,k\) ,問在 \(s_{l\cdots r}\) 中出現(xiàn)了多少次 \(s_k\) 。
\(n,q,\sum|s|\le 10^5\)
分析
先建AC自動機的 \(fail\) 樹, 我們考慮兩種暴力:
- 將 \(l?\) 到 \(r?\) 中的每個串的末尾節(jié)點子樹標記,查詢 \(s_k?\) 的所有節(jié)點 \(fail?\) 樹到根的路徑和。
- 將 \(s_k\) 的每個節(jié)點的子樹標記,查詢 \(l\) 到 \(r\) 中的每個末尾節(jié)點的點權和。
發(fā)現(xiàn)這兩種做法在不同的數(shù)據(jù)下有著不同的效果,考慮根號分治:
- 如果 \(|s_k|\le\sqrt n\) 采用第一種方式,差分查詢,這樣操作每個串的次數(shù)不超過 \(\sqrt n\) ,動態(tài)維護前綴和。
- 如果 \(|s_k|>\sqrt n\) 采用第二種方式,記錄前綴和即可,這樣的串不超過 \(\sqrt n\) 個。
我們發(fā)現(xiàn),對于 \(dfs\) 序數(shù)組來說,修改次數(shù)是 \(O(n)\) 級別,但是查詢次數(shù)卻是 \(O(n\sqrt n)\) 級別的,能不能平衡兩種操作時間復雜度呢?
考慮分塊來維護前綴和,每個塊維護一個加標記。這樣修改變成了 \(O(\sqrt n)\) ,但是查詢變成了 \(O(1)\) 。
總時間復雜度為 \(O(n\sqrt n)\)。
代碼
#include<bits/stdc++.h> using namespace std; typedef long long LL; #define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to) #define rep(i, a, b) for(int i = a; i <= b; ++i) #define pb push_back #define re(x) memset(x, 0, sizeof x) inline int gi() {int x = 0,f = 1;char ch = getchar();while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}return x * f; } template <typename T> inline void Max(T &a, T b){if(a < b) a = b;} template <typename T> inline void Min(T &a, T b){if(a > b) a = b;} const int N = 1e5 + 7; int n, q, sz = 317, tim; int L[N], R[N], in[N], out[N], ch[N][26]; char s[N]; namespace tr{int edc;int head[N];struct edge {int lst, to;edge(){}edge(int lst, int to):lst(lst), to(to) {}}e[N];void Add(int a, int b) {e[++edc] = edge(head[a], b), head[a] = edc;}void dfs(int u) {in[u] = ++tim; go(u) dfs(v); out[u] = tim;} } namespace ac {int endp[N], fail[N], ndc;int idx(char c) { return c - 'a';}void ins(int a) {L[a] = R[a - 1] + 1;scanf("%s", s + L[a]);R[a] = L[a] + strlen(s + L[a]) - 1;int u = 0;for(int i = L[a]; i <= R[a]; ++i) {int c = idx(s[i]);if(!ch[u][c]) ch[u][c] = ++ndc;u = ch[u][c];}endp[a] = u;}void getfail() {queue<int>Q;for(int c = 0; c < 26; ++c) if(ch[0][c]) Q.push(ch[0][c]), tr::Add(0, ch[0][c]);while(!Q.empty()) {int u = Q.front();Q.pop();for(int c = 0; c < 26; ++c) {int &v = ch[u][c];if(!v) { v = ch[fail[u]][c]; continue;}fail[v] = ch[fail[u]][c];Q.push(v);tr::Add(fail[v], v);}}} } struct data {int l, r, k, id, opt;bool operator <(const data &rhs) const {return r < rhs.r;} }t[N << 1]; vector<data>G[N]; int qc, bl[N];//時間戳數(shù)組長度為tim int Rp(int x){ return min(tim, x * sz);} LL ans[N], sum[N], pre[N], add[400]; void mdf(int p, int v) {if(p == tim + 1) return;for(int i = p; i <= Rp(bl[p]); ++i) pre[i] +=v;for(int i = bl[p] + 1; i <= bl[tim]; ++i) add[i] += v; } LL qry(int p) { return pre[p] + add[bl[p]]; } void modify(int l, int r) { mdf(l, 1); mdf(r + 1, -1);} LL query(int l, int r) { return qry(r) - qry(l - 1); } void solve(int x) {re(sum), re(pre), re(add);int u = 0;for(int i = L[x]; i <= R[x]; ++i) {u = ch[u][ac::idx(s[i])];mdf(in[u], 1);}for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + query(in[ac::endp[i]], out[ac::endp[i]]);for(auto v: G[x]) {ans[v.id] = sum[v.r] - sum[v.l - 1];} } void Addstring(int x) {int u = ac::endp[x];modify(in[u], out[u]); } LL Substring(int x) {int u = 0;LL ans = 0;for(int i = L[x]; i <= R[x]; ++i) {u = ch[u][ac::idx(s[i])];ans += qry(in[u]);}return ans; } int main() {n = gi(), q = gi();rep(i, 1, n) ac::ins(i);ac::getfail(), tr::dfs(0);rep(i, 1, tim) bl[i] = (i - 1) / sz + 1;rep(i, 1, q) {int l = gi(), r = gi(), k = gi();if(R[k] - L[k] + 1 <= sz) {t[++qc] = (data){ 0, l - 1, k, i, -1 };t[++qc] = (data){ 0, r, k, i, 1 };}elseG[k].pb((data){ l, r, k, i, 1});}rep(i, 1, n) if(R[i] - L[i] + 1 > sz) solve(i);re(pre), re(add);sort(t + 1, t + 1 + qc);for(int i = 0, j = 1; i <= n; ++i) {if(i) Addstring(i);for(; j <= qc && t[j].r == i; ++j) {ans[t[j].id] += 1ll * t[j].opt * Substring(t[j].k);}}rep(i, 1, q) printf("%lld\n", ans[i]);return 0; }轉載于:https://www.cnblogs.com/yqgAKIOI/p/10171960.html
總結
以上是生活随笔為你收集整理的[CF587F]Duff is Mad[AC自动机+根号分治+分块]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux明日命令(6):rm命令
- 下一篇: Netty源码 服务端的启动