trie树上的回文自动机
本博客只是论文的一小部分。
这个应该算pam最基础的扩展了。例题hdu5394
直接暴力插入的复杂度肯定是不对的,是n^2的,我们需要优化找fail那个函数。
也就是说,我们要优化这样的操作,对于pam上的一个节点x,找到最长的前驱为c的回文后缀。
特判第一步就找到的情况。否则这个前驱字符一定在x内,可以预处理。每次新插入的点继承他fail的数组。
更具体的细节可以看代码。(也没有细节了)
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<ctime>
#include<iostream>
#include<vector>
#include<cassert>
#include<algorithm>
#include<utility>
#include<queue>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define forg(i,x) for(register int i=fir[x];i;i=nxt[i])
#define uu unsigned
#define scanf a14=scanf
#define rint register int
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
typedef long long ll;
typedef uu long long ull;
typedef pair<int,int> pii;
int a14;
inline int rd(int l,int r){return rand()%(r-l+1)+l;}
const int mxn=2e6+5;
int len[mxn],fail[mxn],ts[mxn][4],tt,po[mxn],n,co[mxn],cnt[mxn],ju[mxn][4];
vector<int>g[mxn];
int s[mxn];
inline int gf(int x,int i){if(s[i]==s[i-len[x]-1])return x; return ju[x][s[i]];}
inline int ext(int id,int c,int n){
int x=gf(id,n);
if(!ts[x][c]){
++tt,len[tt]=len[x]+2;
int y=ts[gf(fail[x],n)][c];if(!y)y=1;
fail[tt]=y;
ju[tt][0]=ju[y][0],ju[tt][1]=ju[y][1],ju[tt][2]=ju[y][2],ju[tt][3]=ju[y][3];
ju[tt][s[n-len[y]]]=y;
ts[x][c]=tt;
}
return ts[x][c];
}
inline void dfs(int x=0,int f=0,int d=0){
if(x)s[d]=co[x]-'a',po[x]=ext(po[f],s[d],d),++cnt[po[x]];
for(int i=0;i<g[x].size();++i)dfs(g[x][i],x,d+1);
}
int main(){
s[0]=666;
int ca;scanf("%d",&ca);while(ca--){
for(int i=0;i<=tt;++i)ts[i][0]=ts[i][1]=ts[i][2]=ts[i][3]=cnt[i]=0;
for(int i=0;i<=n;++i)g[i].clear();
// len[1]=-1,tt=1,fail[0]=fail[1]=1;
len[0]=-1,tt=1;
scanf("%d",&n);for(int i=1;i<=n;++i){
char c[2];int x;scanf("%s%d",c,&x);co[i]=c[0];
g[x].push_back(i);
}
dfs();
ll ans=0;
for(int i=tt;i>1;--i)cnt[fail[i]]+=cnt[i],ans+=1ll*cnt[i]*len[i];
printf("%lld\n",ans);
}
return 0;
}