zoukankan      html  css  js  c++  java
  • [BJWC2018]Border 的四种求法

    description

    luogu
    给一个小写字母字符串(S),(q)次询问每次给出(l,r),求(s[l..r])(Border)

    solution

    我们考虑转化题面:给定(l,r),求满足(lcs(i,r)ge i-l+1)的最大的(i)
    对于(lcs(i,r)),考虑对(S)构建(SAM),那么我们知道(lcs)的可能取值就是从后缀(r)所代表的节点沿着(fail)树到根节点的路径上节点的(len)的取值。
    那么我们得到了一个暴力做法:(SAM)+线段树合并后暴跳(fail)树,
    在每个节点的线段树中查询([l,min(r,len+l-1)))中最大的(i)

    如何优化?
    考虑树链剖分,我们要求出的一条根到父亲的链在(fail)树上是若干段重链的前缀。
    那么可以对每条重链进行离线而分开考虑。
    查询前缀时,除了最后一个点需要查询重儿子及其子树,其余的点都只要查询轻儿子及其子树即可。
    因此查询前缀时对于最后一个点仍然使用(SAM)+线段树合并的做法,
    对于其他的点,考虑将(lcs(i,r)ge i-l+1)变为(i-len[x]+1le l)
    只须从上往下一个个插入轻子树的儿子+回答询问。
    回答询问时,需要建立一棵以(i)为下标,权值为(i-len[x]+1)的最小值线段树,
    查询时需要查询(lle i<r)(i-len[x]+1le l)的最大的(i),
    此时线段树上二分,根据右子树的最小值是否(le l)作出决策,单次时间复杂度降为(O(logn))
    由于每个点到根节点的轻边仅(O(log))条,因此插入的总时间复杂度为(O(nlog^2n))
    线段树处理询问的总时间同样为(O(nlog^2n))
    故总时间复杂度为(O(nlog^2n))

    code

    话说这是我第一次写SAM+线段树合并
    我错了是我人丑常数大1e5还要跑300ms

    #include<bits/stdc++.h>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define FL "a"
    using namespace std;
    typedef unsigned long long ll;
    typedef pair<int,int> PI;
    typedef vector<int> VI;
    typedef long double dd;
    const int N=4e5+10;
    const int mod=1e9+7;
    const int inf=2147483647;
    inline ll read(){
      ll data=0,w=1;char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
      if(ch=='-')w=-1,ch=getchar();
      while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
      return data*w;
    }
    inline void file(){
      freopen(FL".in","r",stdin);
      freopen(FL".out","w",stdout);
    }
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define mid ((l+r)>>1)
    struct node{int l,r,id;};
    int n,q;char s[N];vector<node>Q[N];int ans[N];
    int head[N],nxt[N],to[N],cnt;
    inline void add(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;}
    int lst=1,tot=1,cnts=0,len[N],fa[N],tr[N][26],pos[N],fid[N];
    inline void extend(int c){
      int cur=++tot,u=lst;len[cur]=len[lst]+1;
      while(u&&!tr[u][c])tr[u][c]=cur,u=fa[u];
      if(!u)fa[cur]=1;
      else{
        int v=tr[u][c];
        if(len[v]==len[u]+1)fa[cur]=v;
        else{
          int clone=++tot;len[clone]=len[u]+1;
          memcpy(tr[clone],tr[v],sizeof(tr[clone]));
          fa[clone]=fa[v];fa[v]=fa[cur]=clone;
          while(u&&tr[u][c]==v)tr[u][c]=clone,u=fa[u];
        }
      }
      lst=cur;pos[lst]=++cnts;fid[cnts]=lst;
    }
    namespace s1{
      int rt[N],s[2][20*N],tot=0;
      void Insert(int &i,int l,int r,int p){
        i=++tot;s[0][i]=s[1][i]=0;if(l==r)return;
        p<=mid?Insert(s[0][i],l,mid,p):Insert(s[1][i],mid+1,r,p);
      }
      int Merge(int a,int b){
        if(!a||!b)return a|b;int c=++tot;
        s[0][c]=Merge(s[0][a],s[0][b]);
        s[1][c]=Merge(s[1][a],s[1][b]);
        return c;
      }
      int Query(int i,int l,int r,int x,int y){
        if(!i)return 0;if(l==r)return l;
        if(mid<y&&s[1][i]){
          int res=Query(s[1][i],mid+1,r,x,y);
          return res?res:Query(s[0][i],l,mid,x,y);
        }
        else return x<=mid?Query(s[0][i],l,mid,x,y):0;
      }
      inline void insert(int u){Insert(rt[u],1,n,pos[u]);}
      inline void merge(int u,int v){rt[u]=Merge(rt[u],rt[v]);}
      inline int query(int u,int l,int r){return Query(rt[u],1,n,l,r);}
      void print(int i,int l,int r){
        if(!i)return;if(l==r){printf("%d ",l);return;}
        print(s[0][i],l,mid);print(s[1][i],mid+1,r);
      }
    }
    namespace s2{
      int mn[N<<2],tot;
      void build(int i,int l,int r){
        mn[i]=inf;if(l==r)return;build(ls,l,mid);build(rs,mid+1,r);
      }
      void del(int i,int l,int r,int p){
        mn[i]=inf;if(l==r)return;
        p<=mid?del(ls,l,mid,p):del(rs,mid+1,r,p);
      }
      void insert(int i,int l,int r,int p,int v){
        if(p<l||p>r)return;mn[i]=min(mn[i],v);if(l==r)return;
        p<=mid?insert(ls,l,mid,p,v):insert(rs,mid+1,r,p,v);
      }
      int query(int i,int l,int r,int x,int y,int v){
        if(mn[i]>v)return 0;if(l==r)return mn[i]<=v?l:0;
        if(mid<y&&mn[rs]<=v){
          int res=query(rs,mid+1,r,x,y,v);
          return res?res:query(ls,l,mid,x,y,v);
        }
        else return x<=mid?query(ls,l,mid,x,y,v):0;
      }
    }
    int sz[N],dep[N],son[N],top[N];
    void dfs1(int u){
      sz[u]=1;dep[u]=dep[fa[u]]+1;son[u]=0;
      for(int i=head[u],v;i;i=nxt[i]){
        dfs1(v=to[i]);sz[u]+=sz[v];
        if(sz[son[u]]<sz[v])son[u]=v;
      }
    }
    void dfs2(int u,int tp){
      top[u]=tp;if(pos[u])s1::insert(u);
      if(son[u])dfs2(son[u],tp),s1::merge(u,son[u]);
      for(int i=head[u],v;i;i=nxt[i])
        if((v=to[i])!=son[u])dfs2(v,v),s1::merge(u,v);
    }
    void ins(int u,int len){
      if(pos[u])s2::insert(1,1,n,pos[u],pos[u]-len+1);
      for(int i=head[u],v;i;i=nxt[i])ins(v=to[i],len);
    }
    void del(int u){
      if(pos[u])s2::del(1,1,n,pos[u]);
      for(int i=head[u],v;i;i=nxt[i])del(v=to[i]);
    }
    inline void getans(int x){
      vector<int>a;a.clear();int m=0;
      for(;x;x=son[x])a.pb(x),m++;
      for(int k=0,u;k<m;k++){    
        u=a[k];
        if(pos[u])s2::insert(1,1,n,pos[u],pos[u]-len[u]+1);
        for(int i=head[u],v;i;i=nxt[i]){
          v=to[i];if(v==son[u])continue;ins(v,len[u]);
        }
        for(int i=0,sz=Q[u].size(),l,r,id,res;i<sz;i++){
          l=Q[u][i].l;r=Q[u][i].r;id=Q[u][i].id;
          res=s2::query(1,1,n,l,r-1,l);
          if(l<=res&&res<r)ans[id]=max(ans[id],res-l+1);
          res=s1::query(u,l,min(len[u]+l-1,r-1));
          if(l<=res&&res<r)ans[id]=max(ans[id],res-l+1);
        }
      }
      for(int k=0,u;k<m;k++){
        u=a[k];if(pos[u])s2::del(1,1,n,pos[u]);
        for(int i=head[u],v;i;i=nxt[i]){
          v=to[i];if(v==son[u])continue;del(v);
        }
      }
      for(int k=0,u;k<m;k++){
        u=a[k];
        for(int i=head[u],v;i;i=nxt[i]){
          v=to[i];if(v==son[u])continue;
          getans(v);
        }
      }
    }
    inline void init(){
      scanf("%s",s+1);n=strlen(s+1);
      for(int i=1;i<=n;i++)extend(s[i]-'a');
      for(int i=2;i<=tot;i++)add(fa[i],i);
      dfs1(1);dfs2(1,1);s2::build(1,1,n);
    }
    inline void solve(){
      q=read();
      for(int i=1,l,r,u;i<=q;i++){
        l=read();r=read();u=fid[r];
        while(u)Q[u].pb((node){l,r,i}),u=fa[top[u]];
      }
      getans(1);
      for(int i=1;i<=q;i++)printf("%d
    ",ans[i]);
    }
    int main()
    {
      init();
      solve();
      return 0;
    }
    
  • 相关阅读:
    在zend framework框架中try{}catch(Exception e){}的跳转问题
    【上】安全HTTPS-全面具体解释对称加密,非对称加密,数字签名,数字证书和HTTPS
    iOS 图像处理-剪裁图像
    Delphi DBGrid记录全选和反选拖动处理
    在DbGrid中,不按下Ctrl,单击鼠标如何实现多选?谢谢
    在DBGrid中实现多选功能
    回车跳到下一个EDIT
    远程控制篇:用Delphi模拟键盘输入/鼠标点击
    SQL的拼接语句在DELPHI中怎么写
    Delphi DbGridEh实现表格没有内容的渐变效果
  • 原文地址:https://www.cnblogs.com/cjfdf/p/10260758.html
Copyright © 2011-2022 走看看