CF587F Duff is Mad 題解
阿新 • • 發佈:2021-06-29
難搞的題啊……做了一下午
使用到了主席樹,AC自動機,根號分治,樹狀陣列等演算法及技巧。
可以想到一個比較明顯的做法,就是建立出AC自動機,然後將fail樹的dfs序處理出來,建立線段樹。對於選取的 \(s[l,r]\) 這些字串,將結束點的子樹加一,然後列舉 s[k] 線上段樹裡面查詢答案。
發現這樣的話複雜度可以達到優秀的 \(n^2logn\) 。考慮使用主席樹,可以降到 \(n^2logn\) (啥也沒變啊喂!)
考慮根號分治,對於 \(len_k <= T\) 的部分,使用上面的演算法可以做到 \(Tlogn\) 的單次查詢。
對於 \(len_k > T\) 的部分,我們首先處理一個數組 \(dp[i][j]\)
我們列舉每一個長串,標記串上每一個點,然後對於每一個終止點,在其fail樹中的子樹裡進行查詢。
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> #define mid (l+r>>1) using namespace std; int read() { int a = 0,x = 1;char ch = getchar(); while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();} while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();} return a*x; } const int N=1e5+7,T=228; int n,q,m[N];unsigned s[N/T+7][N]; char S[N]; vector<int>arr[N],g; int ch[N][26],tot=1,cnt,pos[N],nxt[N]; int add(char *s,vector<int>&a) { int t = 1,len = strlen(s+1); for(int i = 1;i <= len;i ++) { if(!ch[t][s[i]-'a']) ch[t][s[i]-'a'] = ++tot; t = ch[t][s[i]-'a'];a.push_back(t); } return t; } void get_fail() { for(int i = 0;i < 26;i ++) ch[0][i] = 1; queue<int>q;q.push(1); while(!q.empty()) { int u = q.front();q.pop(); for(int i = 0;i < 26;i ++) { if(!ch[u][i]) ch[u][i] = ch[nxt[u]][i]; else { nxt[ch[u][i]] = ch[nxt[u]][i]; q.push(ch[u][i]); } } } } int head[N],go[N],nt[N]; void add(int u,int v) { go[++cnt] = v; nt[cnt] = head[u]; head[u] = cnt; } int dfn[N],siz[N]; void dfs(int u) { dfn[u] = ++cnt,siz[u] = 1; for(int e = head[u];e;e = nt[e]){ dfs(go[e]);siz[u] += siz[go[e]]; } } int tre[N<<5],tag[N<<5],rt[N],ls[N<<5],rs[N<<5]; void push_down(int root,int l,int r) { tag[root<<1|1] += tag[root],tag[root<<1] += tag[root]; tre[root<<1] += (mid-l+1)*tag[root],tre[root<<1|1] += (r-mid)*tag[root]; tag[root] = 0; } void modify(int root,int l,int r,int ql,int qr,int x) { if(l >= ql && r <= qr) {tag[root] += x,tre[root] += (r-l+1)*x;return ;} if(l > qr || r < ql) return ; push_down(root,l,r); modify(root<<1,l,mid,ql,qr,x);modify(root<<1|1,mid+1,r,ql,qr,x); tre[root] = tre[root<<1|1] + tre[root<<1]; } int query(int root,int l,int r,int ql,int qr) { if(l >= ql && r <= qr) return tre[root]; if(l > qr || r < ql) return 0; push_down(root,l,r); return query(root<<1,l,mid,ql,qr) + query(root<<1|1,mid+1,r,ql,qr); } void modify(int r1,int &r2,int l,int r,int ql,int qr,int x) { if(l > qr || r < ql) return ; r2 = ++cnt;ls[r2] = ls[r1],rs[r2] = rs[r1];tre[r2] = tre[r1];tag[r2] = tag[r1]; if(l >= ql && r <= qr) {tre[r2] += (r-l+1)*x,tag[r2] += x;return ;} modify(ls[r1],ls[r2],l,mid,ql,qr,x);modify(rs[r1],rs[r2],mid+1,r,ql,qr,x); tre[r2] += x*(min(r,qr)-max(l,ql)+1); } int query(int r1,int r2,int l,int r,int ql,int qr) { if(l >= ql && r <= qr) return tre[r2]-tre[r1]; if(l > qr || r < ql) return 0; return (min(qr,r)-max(ql,l)+1)*(tag[r2]-tag[r1]) + query(ls[r1],ls[r2],l,mid,ql,qr) + query(rs[r1],rs[r2],mid+1,r,ql,qr); } int id[N],B[N]; void add1(int p,int a,int *B) { for(int i = p;i <= tot;i += i&-i) B[i] += a; } int query(int p,int *B) { int ret = 0; for(int i = p;i >=1;i -= i&-i) ret += B[i]; return ret; } int main() { // freopen("random.in","r"/,stdin); // freopen("sol.out","w",stdout); n = read(),q = read(); for(int i = 1;i <= n;i ++) { scanf("%s",S+1); pos[i] = add(S,arr[i]);m[i] = strlen(S+1); if(m[i] >= T) g.push_back(i),id[i] = g.size(); } get_fail(); for(int i = 2;i <= tot;i ++) add(nxt[i],i); cnt = 0;dfs(1);cnt=0; for(int o:g) { ++cnt; for(auto u:arr[o]) { add1(dfn[u],1,B); } for(int i = 1;i <= n;i ++) { s[cnt][i] = s[cnt][i-1] + query(dfn[pos[i]]+siz[pos[i]]-1,B) - query(dfn[pos[i]]-1,B); } for(auto u:arr[o]) { add1(dfn[u],-1,B); } } cnt = 0;for(int i = 1;i <= n;i ++) { modify(rt[i-1],rt[i],1,tot,dfn[pos[i]],dfn[pos[i]]+siz[pos[i]]-1,1); } for(int i = 1;i <= q;i ++) { int l = read(),r = read(),k = read(); if(m[k] >= T) { printf("%u\n",s[id[k]][r]-s[id[k]][l-1]); } else { int ret = 0; for(int x:arr[k]) { int qwq = query(rt[l-1],rt[r],1,tot,dfn[x],dfn[x]); ret += qwq; } printf("%d\n",ret); } } return 0; }