【[NOI2011]阿貍的打字機】
阿新 • • 發佈:2019-01-01
發現 swap num lin wap != lowbit def uil 序,之後把問題轉化為單點加,區間查顯然可以直接用一個樹狀數組來維護
首先發現這個插入的非常有特點,我們可以直接利用這個特殊的性質在\(Trie\)樹上模擬指針的進退
之後得到了\(Trie\)樹,先無腦建出\(AC\)機
之後考慮一下如何寫暴力
最簡單的暴力對於每一個詢問直接在\(AC\)機上匹配之後跳\(fail\),跳到多少次\(fail\)就代表出現了幾次
顯然這並不能通過
考慮一下優雅的跳\(fail\)
發現\(fail\)指針建出來恰好是一個樹的結構,因為一個點的\(fail\)只能指向唯一的一個點
把這樣一棵\(fail\)樹建出來,我們直接在\(fail\)樹上判斷另一個串的結束標記是否在這個點到根的路徑上就好了
可以對\(fail\)樹搞一個\(dfs\)
但是這個樣子還是要對每一個串都進行一遍這樣的操作
但是考慮到每一個查詢操作有很多共用的節點,我們可以直接按照\(Trie\)上的順序離線下來,之後利用大量重復的這一特性去統計答案
代碼
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<queue> #define re register #define maxn 100005 #define LL long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define lowbit(x) ((x)&(-x)) struct E { int v,nxt; }e[maxn<<1]; int head[maxn],fa[maxn]; int n,m,to[maxn],dfn[maxn],DFN[maxn]; int cnt,num,tot,__,t; struct Ask { int x,y,rk,ans; }a[maxn]; inline int read() { char c=getchar(); int x=0; while(c<‘0‘||c>‘9‘) c=getchar(); while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } inline void add_edge(int x,int y) { e[++num].v=y; e[num].nxt=head[x]; head[x]=num; } char S[maxn]; int c[maxn],sum[maxn]; inline void add(int x,int val){for(re int i=x;i<=cnt+1;i+=lowbit(i)) c[i]+=val;} inline int ask(int x){int now=0;for(re int i=x;i;i-=lowbit(i)) now+=c[i];return now;} int son[maxn][26],trie[maxn][26],fail[maxn],Ans[maxn]; inline void ins() { int now=0; fa[now]=0; scanf("%s",S+1); int len=strlen(S+1); for(re int i=1;i<=len;i++) { if(S[i]==‘B‘) { now=fa[now]; continue; } if(S[i]==‘P‘) { to[++tot]=now; continue; } if(!son[now][S[i]-‘a‘]) son[now][S[i]-‘a‘]=trie[now][S[i]-‘a‘]=++cnt,fa[cnt]=now; now=son[now][S[i]-‘a‘]; } } inline void Build() { std::queue<int> q; for(re int i=0;i<26;i++) if(son[0][i]) q.push(son[0][i]); while(!q.empty()) { int k=q.front();q.pop(); for(re int i=0;i<26;i++) if(son[k][i]) fail[son[k][i]]=son[fail[k]][i],q.push(son[k][i]); else son[k][i]=son[fail[k]][i]; } } inline int cmp(Ask A,Ask B) { return dfn[A.x]<dfn[B.x]; } void DFS(int x) { dfn[x]=++__; if(x!=fail[x]) add_edge(fail[x],x);//printf("%d\n",x); for(re int i=0;i<26;i++) if(trie[x][i]) DFS(trie[x][i]); } void dfs(int x,int F) { sum[x]=1;DFN[x]=++__; for(re int i=head[x];i;i=e[i].nxt) if(e[i].v!=F) { dfs(e[i].v,x); sum[x]+=sum[e[i].v]; } } void Dfs(int x) { __++; add(DFN[x],1); while(a[t].x==x) { int Y=to[a[t].y]; a[t].ans=ask(DFN[Y]+sum[Y]-1)-ask(DFN[Y]-1); t++; } for(re int i=0;i<26;i++) if(trie[x][i]) Dfs(trie[x][i]); add(DFN[x],-1); } int main() { ins(); Build(),DFS(0); n=read(); for(re int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].rk=i,std::swap(a[i].x,a[i].y),a[i].x=to[a[i].x]; std::sort(a+1,a+n+1,cmp); __=0,dfs(0,0),__=0;t=1; Dfs(0); for(re int i=1;i<=n;i++) Ans[a[i].rk]=a[i].ans; for(re int i=1;i<=n;i++) printf("%d\n",Ans[i]); return 0; }
【[NOI2011]阿貍的打字機】