【XSY2715】回文串 树链剖分 回文自动机

题目描述

  有一个字符串 s ,长度为n。有 m 个操作:

  • addl c:在 s 左边加上一个字符c

    • addr c :在 s 右边加上一个字符
    • transl l1 r1 l2 r2:有两个 s 的子串s1=s[l1r1],s2=s[l2r2]。你要把 s1 变成 s2 。每次允许在左边加一个字符或删一个字符。要求操作次数最少。定义一个字符串是好的当且仅当这个字符串是回文串且在操作过程中出现。记
    • ans=i=1nj=in[s[ij] is good]×(ji+1)

        求 ans

      • transr l1 r1 l2 r2 :和上一个操作类似,但是只允许在右边加入或删除字符

      <Empty Math Block>

         n,m100000

      题解

        毒瘤题。

        先把所有操作保存下来,求出最终的字符串。

        观察到操作过程中每个字符串都只会出现一次,而且左边的一部分相同。

        可以用回文自动机完成操作。

        先把回文自动机建出来,把fail树剖分。

        处理出每个前缀/后缀的最长回文子串。

        询问时先在fail树上面跳得到在范围内的最长回文子串。

        那么答案就是这条链上所有回文串的出现次数 × 长度的和。

        如果lca不是这两个串的lcp,那么就要减掉lca的值。

        插入时找到在当前范围内的最长回文前缀/后缀,把这个点到根上所有字符串的出现次数 +1

        时间复杂度: O((n+m)log2n)

        其实开始给你的那个字符串可以在 O(n) 内预处理完成的,总的复杂度就是 O(n+mlog2n) ,不过我懒得写了。

      代码

      #include<cstdio>
      #include<cstring>
      #include<algorithm>
      using namespace std;
      typedef long long ll;
      namespace pam
      {
          int s[200010];
          int next[200010][30];
          int fail[200010];
          int len[200010];
          int last;
          int n;
          int p;
          void init(int x)
          {
              int i;
              for(i=1;i<=26;i++)
                  next[x][i]=2;
          }
          void init()
          {
              init(1);
              init(2);
              len[1]=-1;
              fail[1]=2;
              len[2]=0;
              fail[2]=1;
              s[0]=0;
              p=2;
              n=0;
              last=2;
          }
          void insert(int x)
          {
              s[++n]=x;
              while(s[n-len[last]-1]!=s[n])
                  last=fail[last];
              int cur=last;
              if(next[cur][x]==2)
              {
                  int now=++p;
                  init(now);
                  len[now]=len[cur]+2;
                  last=fail[last];
                  while(s[n-len[last]-1]!=s[n])
                      last=fail[last];
                  fail[now]=next[last][x];
                  next[cur][x]=now;
              }
              last=next[cur][x];
          }
          void rebuild()
          {
              last=2;
              n=0;
          }
          void insert2(int x)
          {
              s[++n]=x;
              while(s[n-len[last]-1]!=s[n])
                  last=fail[last];
              int cur=last;
              last=next[cur][x];
          }
      }
      namespace sgt
      {
          struct node
          {
              int l,r,ls,rs;
              ll v;
              ll s;
              int t;
          };
          node a[400010];
          int n;
          int rt;
          void build(int &p,int l,int r,int *v)
          {
              p=++n;
              a[p].l=l;
              a[p].r=r;
              if(l==r)
              {
                  a[p].v=v[l];
                  return;
              }
              int mid=(l+r)>>1;
              build(a[p].ls,l,mid,v);
              build(a[p].rs,mid+1,r,v);
              a[p].v=a[a[p].ls].v+a[a[p].rs].v;
          }
          void add(int p,int t)
          {
              a[p].t+=t;
              a[p].s+=a[p].v*t;
          }
          void push(int p)
          {
              if(a[p].t)
              {
                  add(a[p].ls,a[p].t);
                  add(a[p].rs,a[p].t);
                  a[p].t=0;
              }
          }
          void add(int p,int l,int r,int v)
          {
              if(l<=a[p].l&&r>=a[p].r)
              {
                  add(p,v);
                  return;
              }
              push(p);
              int mid=(a[p].l+a[p].r)>>1;
              if(l<=mid)
                  add(a[p].ls,l,r,v);
              if(r>mid)
                  add(a[p].rs,l,r,v);
              a[p].s=a[a[p].ls].s+a[a[p].rs].s;
          }
          ll query(int p,int l,int r)
          {
              if(l<=a[p].l&&r>=a[p].r)
                  return a[p].s;
              int mid=(a[p].l+a[p].r)>>1;
              push(p);
              ll s=0;
              if(l<=mid)
                  s+=query(a[p].ls,l,r);
              if(r>mid)
                  s+=query(a[p].rs,l,r);
              return s;
          }
      }
      using sgt::rt;
      namespace tree
      {
          struct graph
          {
              int h[200010];
              int v[200010];
              int t[200010];
              int n;
              void add(int x,int y)
              {
                  n++;
                  v[n]=y;
                  t[n]=h[x];
                  h[x]=n;
              }
          };
          graph g;
          void addedge(int x,int y)
          {
              g.add(x,y);
          }
          int f[200010];
          int d[200010];
          int w[200010];
          int t[200010];
          int s[200010];
          int ms[200010];
          int c[200010];
          int v[200010];
          int e[200010];
          int ti;
          void dfs1(int x,int fa,int dep)
          {
              f[x]=fa;
              d[x]=dep;
              s[x]=1;
              int mx=0;
              int i;
              for(i=g.h[x];i;i=g.t[i])
              {
                  dfs1(g.v[i],x,dep+1);
                  s[x]+=s[g.v[i]];
                  if(s[g.v[i]]>mx)
                  {
                      mx=s[g.v[i]];
                      ms[x]=g.v[i];
                  }
              }
          }
          void dfs2(int x,int top)
          {
              t[x]=top;
              w[x]=++ti;
              c[ti]=x;
              e[ti]=v[x];
              if(!ms[x])
                  return;
              dfs2(ms[x],top);
              int i;
              for(i=g.h[x];i;i=g.t[i])
                  if(g.v[i]!=ms[x])
                      dfs2(g.v[i],g.v[i]);
          }
          int find(int x,int y)
          {
              while(v[t[x]]>y)
                  x=f[t[x]];
              return c[upper_bound(e+w[t[x]],e+w[x]+1,y)-e-1];
          }
          void add(int x,int v)
          {
              while(x)
              {
                  sgt::add(rt,w[t[x]],w[x],v);
                  x=f[t[x]];
              }
          }
          ll query(int x,int y)
          {
              ll s=0;
              while(t[x]!=t[y])
                  if(d[t[x]]>d[t[y]])
                  {
                      s+=sgt::query(rt,w[t[x]],w[x]);
                      x=f[t[x]];
                  }
                  else
                  {
                      s+=sgt::query(rt,w[t[y]],w[y]);
                      y=f[t[y]];
                  }
              if(w[x]<w[y])
                  s+=sgt::query(rt,w[x],w[y]);
              else
                  s+=sgt::query(rt,w[y],w[x]);
              return s;
          }
          int getlca(int x,int y)
          {
              while(t[x]!=t[y])
                  if(d[t[x]]>d[t[y]])
                      x=f[t[x]];
                  else
                      y=f[t[y]];
              return w[x]<w[y]?x:y;
          }
      }
      int op[100010],ql1[100010],qr1[100010],ql2[100010],qr2[100010];
      int n,m;
      int s[200010],s1[100010],s2[100010],s3[100010];
      int pre[200010],suf[200010];
      int nl,nr;
      void addr()
      {
          nr++;
          int x=tree::find(pre[nr],nr-nl+1);
          tree::add(x,1);
      }
      void addl()
      {
          nl--;
          int x=tree::find(suf[nl],nr-nl+1);
          tree::add(x,1);
      }
      void queryl(int l1,int r1,int l2,int r2)
      {
          int x=tree::find(pre[r1],r1-l1+1);
          int y=tree::find(pre[r2],r2-l2+1);
          int lca=tree::getlca(x,y);
          ll ans=tree::query(x,y);
          int len=tree::v[lca];
          if(r1-l1+1!=len&&r2-l2+1!=len&&s[r1-len]==s[r2-len])
              ans-=sgt::query(rt,tree::w[lca],tree::w[lca]);
          printf("%lld\n",ans);
      }
      void queryr(int l1,int r1,int l2,int r2)
      {
          int x=tree::find(suf[l1],r1-l1+1);
          int y=tree::find(suf[l2],r2-l2+1);
          int lca=tree::getlca(x,y);
          ll ans=tree::query(x,y);
          int len=tree::v[lca];
          if(r1-l1+1!=len&&r2-l2+1!=len&&s[l1+len]==s[l2+len])
              ans-=sgt::query(rt,tree::w[lca],tree::w[lca]);
          printf("%lld\n",ans);
      }
      int main()
      {
      #ifndef ONLINE_JUDGE
          freopen("b.in","r",stdin);
          freopen("b.out","w",stdout);
      #endif
          char str[10];
          scanf("%d%d",&n,&m);
          int n1=0,n2=0;
          int i;
          for(i=1;i<=n;i++)
          {
              scanf("%d",&s3[i]);
              s3[i]++;
          }
          for(i=1;i<=m;i++)
          {
              scanf("%s",str);
              if(str[0]=='a')
              {
                  scanf("%d",&ql1[i]);
                  ql1[i]++;
                  if(str[3]=='l')
                  {
                      op[i]=1;
                      s1[++n1]=ql1[i];
                  }
                  else
                  {
                      op[i]=2;
                      s2[++n2]=ql1[i];
                  }
              }
              else
              {
                  scanf("%d%d%d%d",&ql1[i],&qr1[i],&ql2[i],&qr2[i]);
                  if(str[5]=='l')
                      op[i]=3;
                  else
                      op[i]=4;
              }
          }
          int num=0;
          for(i=m;i>=1;i--)
          {
              if(op[i]==1)
                  num++;
              else if(op[i]>=2)
              {
                  ql1[i]+=num;
                  qr1[i]+=num;
                  ql2[i]+=num;
                  qr2[i]+=num;
              }
          }
          int len=0;
          for(i=n1;i>=1;i--)
              s[++len]=s1[i];
          for(i=1;i<=n;i++)
              s[++len]=s3[i];
          for(i=1;i<=n2;i++)
              s[++len]=s2[i];
          pam::init();
          for(i=1;i<=len;i++)
          {
              pam::insert(s[i]);
              pre[i]=pam::last;
          }
          pam::rebuild();
          for(i=len;i>=1;i--)
          {
              pam::insert2(s[i]);
              suf[i]=pam::last;
          }
          for(i=2;i<=pam::p;i++)
          {
              tree::addedge(pam::fail[i],i);
              tree::v[i]=pam::len[i];
          }
          tree::dfs1(1,0,1);
          tree::dfs2(1,1);
          sgt::build(rt,1,pam::p,tree::e);
          nl=n1+1;
          nr=n1;
          for(i=1;i<=n;i++)
              addr();
          for(i=1;i<=m;i++)
              if(op[i]==1)
                  addl();
              else if(op[i]==2)
                  addr();
              else if(op[i]==3)
                  queryl(ql1[i],qr1[i],ql2[i],qr2[i]);
              else
                  queryr(ql1[i],qr1[i],ql2[i],qr2[i]);
          return 0;
      }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值