[后缀自动机 后缀树 虚树] BZOJ 3879 SvT

挺无脑的一个题
直接反串后缀自动机建出后缀树 然后询问建虚树

#include
#include
#include
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(char *s){
  char c=nc(); int len=0;
  for (;!(c>='a' && c<='z');c=nc());
  for (;c>='a' && c<='z';s[++len]=c,c=nc()); s[++len]=0;
}

const int N=1000005;

struct state{
  int link,len,next[26];
}st[N];
int ncnt,last;
inline int Extend(int c){
  int cur=++ncnt,p;
  st[cur].len=st[last].len+1;
  for (p=last;~p && !st[p].next[c];p=st[p].link)
    st[p].next[c]=cur;
  if (p==-1)
    st[cur].link=0;
  else{
    int q=st[p].next[c];
    if (st[p].len+1==st[q].len)
      st[cur].link=q;
    else{
      int nq=++ncnt;
      st[nq].link=st[q].link;
      st[nq].len=st[p].len+1;
      for (int i=0;i<26;i++) st[nq].next[i]=st[q].next[i];
      for (;~p && st[p].next[c]==q;p=st[p].link)
    st[p].next[c]=nq;
      st[q].link=st[cur].link=nq;
    }
  }
  return last=cur;
}

struct edge{
  int u,v,next;
}G[N<<2];
int head[N],inum;
inline void add(int u,int v,int p,int *head=::head){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}
#define V G[p].v

const int K=21;

int fat[N][K],depth[N];
int pre[N],clk;

inline void dfs(int u,int fa){
  fat[u][0]=fa; depth[u]=depth[fa]+1; pre[u]=++clk;
  for (int i=1;i1]][i-1];
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa)
      dfs(V,u);
}
inline int LCA(int u,int v){
  if (depth[u]for (int k=K-1;~k;k--)
    if ((depth[u]-depth[v])>>k&1)
      u=fat[u][k];
  if (u==v) return u;
  for (int k=K-1;~k;k--)
    if (fat[u][k]!=fat[v][k])
      u=fat[u][k],v=fat[v][k];
  return fat[u][0];
}

int rhead[N];
int size[N],val[N];
ll ans=0;

inline void find(int u,int fa){
  size[u]=val[u];
  for (int p=rhead[u];p;p=G[p].next)
    if (V!=fa){
      find(V,u);
      ans+=(ll)size[u]*size[V]*st[u].len;
      size[u]+=size[V];
    }
  rhead[u]=0;
}

int n,Q;
int idx[N];
char s[N];
int m,a[N];

inline bool cmp(int x,int y){
  return pre[x]y];
}

int sta[N],pnt;

inline void Solve(){
  int tmp=inum;
  read(m); for (int i=1;i<=m;i++) read(a[i]),a[i]=idx[a[i]];
  sort(a+1,a+m+1,cmp); m=unique(a+1,a+m+1)-a-1;
  for (int i=1;i<=m;i++) val[a[i]]=1;
  sta[0]=-1;
  for (int i=1;i<=m;i++){
    if (!pnt) { sta[++pnt]=a[i]; continue; }
    int lca=LCA(sta[pnt],a[i]);
    while (pnt && depth[lca]if (depth[lca]>=depth[sta[pnt-1]]){
    add(lca,sta[pnt],++inum,rhead);
    if (sta[--pnt]!=lca) sta[++pnt]=lca;
    break;
      }
      pnt--; add(sta[pnt],sta[pnt+1],++inum,rhead);
    }
    sta[++pnt]=a[i];
  }
  while (--pnt) add(sta[pnt],sta[pnt+1],++inum,rhead);
  ans=0;
  find(sta[1],-1);
  printf("%lld\n",ans);
  inum=tmp;
  for (int i=1;i<=m;i++) val[a[i]]=0;
}

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(Q); read(s);
  st[0].link=-1;
  for (int i=n;i;i--) idx[i]=Extend(s[i]-'a');
  for (int i=1;i<=ncnt;i++) add(st[i].link,i,++inum);
  dfs(0,-1);
  while (Q--)
    Solve();
  return 0;
}

你可能感兴趣的:(虚树,后缀自动机)