Wannafly挑戰賽10F-小H和遺蹟【Trie,樹狀陣列】
阿新 • • 發佈:2021-08-20
正題
題目連結:https://ac.nowcoder.com/acm/contest/72/F
題目大意
\(n\)個字串,包括小寫字母和\(\#\)。其中\(\#\)可以替換為任意字串。求有多少對字串可能相同。
保證每個字串至少有一個\(\#\)。
\(2\leq n\leq 500000,\sum_{i=1}^n |s_i|\leq 10^6\)
解題思路
因為可以替換為任意字串,所以只需要考慮第一個\(\#\)前和最後一個\(\#\)後的部分。
在仔細考慮一下,這個字串分成前後的兩個部分\(s,t\)。數對\((x,y)\)滿足條件當且僅當\(s_x\)是\(s_y\)的字首,或者\(s_y\)是\(s_x\)
放到兩棵\(Trie\)樹上就是都有祖先關係就好了,直接跑第一棵上,然後用兩個樹狀陣列在第二棵樹上維護就好了。
時間複雜度\(O(m\log m)\)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define lowbit(x) (x&-x) using namespace std; const int N=1e6+10; int n,cnt,pos[N],dfn[N],ed[N]; long long ans;char s[N]; vector<int> G[N]; struct TreeBinary{ int t[N]; void Change(int x,int val){ while(x<=cnt){ t[x]+=val; x+=lowbit(x); } return; } int Ask(int x){ int ans=0; while(x){ ans+=t[x]; x-=lowbit(x); } return ans; } }Bf,Bs; struct Trie{ int t[N][26],m=1; int Insert(char *s,int n){ int x=1; for(int i=1;i<=n;i++){ if(s[i]=='#')break; int c=s[i]-'a'; if(!t[x][c])t[x][c]=++m; x=t[x][c]; } return x; } }Tp,Ts; void dfs(int x){ dfn[x]=++cnt; for(int i=0;i<26;i++) if(Ts.t[x][i]) dfs(Ts.t[x][i]); ed[x]=cnt; } void work(int x){ for(int i=0;i<G[x].size();i++){ int p=G[x][i];ans+=Bf.Ask(dfn[pos[p]]); Bf.Change(dfn[pos[p]],1);Bf.Change(ed[pos[p]]+1,-1); ans+=Bs.Ask(ed[pos[p]])-Bs.Ask(dfn[pos[p]]); Bs.Change(dfn[pos[p]],1); } for(int i=0;i<26;i++) if(Tp.t[x][i]) work(Tp.t[x][i]); for(int i=0;i<G[x].size();i++){ int p=G[x][i];Bs.Change(dfn[pos[p]],-1); Bf.Change(dfn[pos[p]],-1);Bf.Change(ed[pos[p]]+1,1); } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s+1); int l=strlen(s+1); int x=Tp.Insert(s,l); G[x].push_back(i); reverse(s+1,s+1+l); pos[i]=Ts.Insert(s,l); } dfs(1); work(1); printf("%lld\n",ans); return 0; }