zoukankan      html  css  js  c++  java
  • [NOI2018]你的名字

    题解:

    前68分非常简单

    建立SAM

    另一个串在上面跑,然后求一个树链的并

    我们会发现暴力就是min(l^2,n)的

    所以复杂度最多是nsqrt(n)的

    当然我们也可以nlogn维护

    把所有点按照dfs序排序,每个点到根的距离减去排序后

    相邻两点LCA到根的距离就是树链的并的长度

    不过要注意一下由于有权值

    所以每个点在当前点可能只取了一部分权值

    调试的时候发现 cnt没清空

    ***

    windows下不开O2不会跑到其他数组里,开了O2就会跑到其他数组里

    linux下开不开都会跑过去

    linux下调试不能用register,不然条件断点就炸了

    暴力map注意s[i]-'a'要+1 不然就是0与没有这一位等价了

    代码:

    #include <bits/stdc++.h>
    
    #define IL inline
    
    #define ll long long
    
    #define rint register int
    
    #define rep(i,h,t) for (int i=h;i<=t;i++)
    
    #define dep(i,t,h) for (int i=t;i>=h;i--)
    
    using namespace std;
    
    const int N=4e6;
    
    char s[N];
    
    int cnt3,m,cnt,ve[N];
    
    ll ans,ans2;
    
    int f[N];
    
    struct hz{
    
    int lst,node,fa[N],size[N],len[N],ch[N][26];
    
    hz()
    
    {
    
        lst=1; node=1;
    
    }
    
    IL void extend(int c)
    
    {
    
      int f=lst,p=++node; lst=p;
    
      len[p]=len[f]+1; size[p]=1;
    
      while (f&&!ch[f][c]) ch[f][c]=p,f=fa[f];
    
      if (!f) { fa[p]=1; return;};
    
      int x=ch[f][c],y=++node;
    
      if (len[f]+1==len[x]) {fa[p]=x; node--;return;};
    
      len[y]=len[f]+1; fa[y]=fa[x]; fa[x]=fa[p]=y;
    
      memcpy(ch[y],ch[x],sizeof(ch[x]));
    
      while (f&&ch[f][c]==x) ch[f][c]=y,f=fa[f];
    
    }
    
    IL void dfs(int x,int y)
    
    {
    
    /*  if (f[x]>=y||!x) return;
    
      ve[++cnt]=x;
    
      if (!f[x]) ans-=y-len[fa[x]];
    
      else ans-=y-f[x];
    
      f[x]=y;
    
      dfs(fa[x],len[fa[x]]);*/
    
      while (f[x]<y&&x)
    
      {
    
        ve[++cnt]=x;
    
        if (!f[x]) ans-=y-len[fa[x]];
    
        else ans-=y-f[x];
    
        f[x]=y;
    
        y=len[fa[x]]; x=fa[x];
    
      }
    
    }
    
    }S1,S2;
    
    int main()
    
    {
      freopen("1.in","r",stdin);
      freopen("2.out","w",stdout); 
     // ios::sync_with_stdio(false);
    
      scanf("%s",s);
    
      int l=strlen(s);
    
      rep(i,1,l) S1.extend(s[i-1]-'a');
    
      cin>>m;
    
      rep(i,1,m)
    
      {
    
          int now=1,x,y; ans=0;
    
          scanf("%s%d%d",s,&x,&y);
    
        l=strlen(s); 
    
          int cnt2=0;
    
          rep(i,0,l-1) S2.extend(s[i]-'a');
    
          rep(i,1,S2.node) ans+=S2.len[i]-S2.len[S2.fa[i]];
    
          for (int i=0;i<l;i++)
    
          {
    
              int k=s[i]-'a';
    
            while (!S1.ch[now][k]&&now) 
    
            {
    
                  now=S1.fa[now],cnt2=S1.len[now];
    
                }
    
            if (!now)
    
            {
    
                now=1; cnt2=0;
    
                } else
    
                {
    
                    cnt2++;
    
                    now=S1.ch[now][k];
    
                    S1.dfs(now,min(S1.len[now],cnt2));
    
                }
    
            }
    
                 cout<<ans<<endl;
    
            rep(i,1,cnt)
    
              f[ve[i]]=0;
    
            memset(S2.fa,0,sizeof(S2.fa[1])*(S2.node+10));
    
            memset(S2.ch,0,sizeof(S2.ch[1])*(S2.node+10));
    
            S2.lst=S2.node=1;
            cnt=0;
    
        }
    
      return 0;
    
    }
    View Code

    #updata: 12.26

    当初的这个暴力真是很厉害。。。

    方法1:这题比较容易想到的是广义后缀自动机

    依然先看前60分

    我们可以建立广义后缀自动机,然后size1>0,size0=0的点被算入就可以

    然后我们会发现后缀自动机的建立过程之前点的父亲可能变为当前点

    所以对这么情况特判一下就好了

    然后这种做法需要还原一大串东西

    这个还原超级容易写错。。。我改了半天,要注意各种还原顺序

    #include <bits/stdc++.h>
    #define ll long long
    #define rll register ll
    #define rep(i,h,t) for (rll i=h;i<=t;i++)
    #define dep(i,t,h) for (rll i=t;i>=h;i--)
    #define me(x) memset(x,0,sizeof(x))
    #define mep(x,y) memcpy(x,y,sizeof(y))
    using namespace std;
    const ll N=3e6;
    char s[N];
    ll t[N],a[N],len[N],ch[N][26],ch1[N][26];
    ll lst=1,node=1,fa[N],fa1[N],len1[N];
    bool tt[N],tt1[N];
    struct re{
        ll a,b,c;
    };
    stack<re> Qc,Qf;
    void extend(ll c)
    {
      ll f=lst;
      if (ch[f][c]&&len[ch[f][c]]==len[f]+1)
      { 
        lst=ch[f][c];
        return;
      }
      ll p=++node; lst=p;
      len[p]=len[f]+1; //size[p][pl]=1;
      while (f&&!ch[f][c]) 
        Qc.push((re){f,c,0}),
        ch[f][c]=p,
        f=fa[f];
      if (!f) { fa[p]=1; return;};
      ll x=ch[f][c],y=++node;
      if (len[f]+1==len[x]) {fa[p]=x; node--;return;};
      Qf.push((re){x,fa[x]});
      len[y]=len[f]+1; 
      fa[y]=fa[x]; 
      fa[x]=fa[p]=y;
      tt[y]=tt[x];
      memcpy(ch[y],ch[x],sizeof(ch[x]));
      while (f&&ch[f][c]==x)
      { 
        Qc.push((re){f,c,x});
        ch[f][c]=y,f=fa[f];
      }
    }
    int main()
    {
      freopen("2.in","r",stdin);
      freopen("1.out","w",stdout);
      ios::sync_with_stdio(false);
      cin>>s;
      ll l=strlen(s);
      rep(i,1,l) extend(s[i-1]-'a');
      rep(i,1,node) tt[i]=1;
      lst=1; 
      ll k;
      cin>>k;
      ll node1=node,lst1=lst;
      mep(fa1,fa); mep(ch1,ch);
      rep(i,1,k)
      {
          while (!Qf.empty()) Qf.pop();
          while (!Qc.empty()) Qc.pop();
      //    Qf.clear(); Qc.clear();
          node=node1; lst=lst1;
          ll x,y;
        cin>>s>>x>>y;
        l=strlen(s);
        rep(i,1,l) extend(s[i-1]-'a'); 
        ll ans=0;
        rep(i,node1+1,node) if (!tt[i]) ans+=len[i]-len[fa[i]];
        cout<<ans<<endl; 
       while (!Qc.empty())
        {
            re x=Qc.top(); Qc.pop();
            ch[x.a][x.b]=x.c;
        }
        while (!Qf.empty())
        {
            re x=Qf.top(); Qf.pop();
            fa[x.a]=x.b;
        }
        rep(i,node1+1,node)
        {
          fa[i]=len[i]=tt[i]=0;
          me(ch[i]);
        }
      // mep(ch,ch1);  
     // mep(fa,fa1);
      }
      return 0;
    }
    View Code

    然后这个好像做不了满分。。

    因为改变了儿子之后要重新合并right集合

    因为启发式是均摊的所以这个直接gg了

    方法2:

    广义后缀自动机在这里的缺点是改变了第一个后缀自动机的状态

    所以我们考虑建两个后缀自动机,然后同时在第一个上面跑

    先考虑前68分

    那么跑到一个点时,我们需要知道第一个自动机的len

    这说明第一个自动机上与到当前节点的后缀最长匹配

    但不能直接用这个len,而是$min(len,lstans+1)$

    原因是后缀自动机的$len[ch[x][c]$]可能不是$len[x]+1$

    比如$bababc$这个串

    原先是$bab$,它的$len$是$3$,现在变成$babc$,它的$len$就变成了$7$

    最后答案就等于$$sumlimits_{i=1}^{node2} {MAX(0,len[i]-MAX(lazy[i],len[fa[i]]))}$$

    (注意更新当前点的时候同时更新一下复制点的信息,就是后缀自动机中$len[x]!=len[f]+1$而新增的点

    这个点可以理解成把第二个SAM当前点的信息分到了两个点上)

    然后考虑l,r任意

    首先肯定是可持久化线段树合并处理第一个$SAM$的$right$集合

    然后查询的时候,我们很明显不能根据当前点$ch[x][c]$为不为空来判断

    而是需要判断$ch[x][c]$的right集合在不在区间里面

    既然是右端点,我们肯定要找在r左边的右端点的最大值

    然后刚开始就这么写了发现有bug

    后来想了一下,如果$right$的在里面,但是$right-len+1$在l左边

    但是它的儿子的$right right-len+1 $可能是更优的

    很容易想到我们只需要判断$right-fa[len]+1$在不在l右边就行了

    为什么呢

    因为如果它在右边,说明它比儿子优,如果不在,说明儿子可能比它优但不会劣于它(因为儿子还可以取这个点)

    然后这个还是挺好写的

    #include <bits/stdc++.h>
    using namespace std;
    #define rint register int
    #define IL inline
    #define rep(i,h,t) for (int i=h;i<=t;i++)
    #define dep(i,t,h) for (int i=t;i>=h;i--)
    #define me(x) memset(x,0,sizeof(x))
    #define ll long long
    #define mid ((h+t)>>1)
    #define mep(x,y) memcpy(x,y,sizeof(y))
    namespace IO
    {
        char ss[1<<24],*A=ss,*B=ss;
        IL char gc()
        {
            return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
        }
        template<class T>void read(T &x)
        {
            rint f=1,c;while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
            while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f;
        }
        char sr[1<<24],z[20]; int C=-1,Z;
        template<class T>void wer(T x)
        {
            if (x<0) sr[++C]='-',x=-x; 
            while (z[++Z]=x%10+48,x/=10);
            while (sr[++C]=z[Z],--Z);
        }
        IL void wer1() {sr[++C]=' ';}
        IL void wer2() {sr[++C]='
    ';}
        template<class T>IL void maxa(T &x,T y) { if (x<y) x=y;}
        template<class T>IL void mina(T &x,T y) { if (x>y) x=y;}
        template<class T>IL T MAX(T x,T y){ return x>y?x:y;}
        template<class T>IL T MIN(T x,T y){ return x<y?x:y;}
    }
    using namespace IO;
    const int INF=1e9;
    const int N=1.5e6;
    char s[N];
    struct hz{
        int lst,node,fa[N],size[N],len[N],ch[N][26],jl[N];
        hz() {node=lst=1;}
        void extend(int c)
        {
          int f=lst;
          if (ch[f][c]&&len[ch[f][c]]==len[f]+1)
          { 
            lst=ch[f][c];
            return;
          }
          int p=++node; lst=p; size[p]=1;
          len[p]=len[f]+1;
          while (f&&!ch[f][c]) ch[f][c]=p,f=fa[f];
          if (!f) { fa[p]=1; return;};
          int x=ch[f][c],y=++node;
          if (len[f]+1==len[x]) {fa[p]=x; node--;return;};
          len[y]=len[f]+1; fa[y]=fa[x]; fa[x]=fa[p]=y;
          memcpy(ch[y],ch[x],sizeof(ch[x]));
          while (f&&ch[f][c]==x) ch[f][c]=y,f=fa[f];
        }
    }T1,T2;
    int head[N],n,pos[N],l,rt[N];
    struct re{
        int a,b;
    }e[N*2];
    IL void arr(int x,int y)
    {
        e[++l].a=head[x];
        e[l].b=y;
        head[x]=l;
    }
    struct sgt{
        int v1[N*24],ls[N*24],rs[N*24],num;
        sgt()
        {
            rep(i,0,N*4-1) v1[i]=0;
        }
        IL void updata(int x)
        {
            v1[x]=MAX(v1[ls[x]],v1[rs[x]]);
        }
        int merge(int x,int y)
        {
           if (!x||!y) return x^y;
           int now=++num;
           ls[now]=merge(ls[x],ls[y]);
           rs[now]=merge(rs[x],rs[y]);
           updata(now);
           return now;
        }
        int query2(int x,int h,int t,int h1,int t1)
        {
            if (h1<=h&&t<=t1) return v1[x];
            int ans=0;
            if (h1<=mid) ans=query2(ls[x],h,mid,h1,t1);
            if (mid<t1) maxa(ans,query2(rs[x],mid+1,t,h1,t1));
            return ans;
        }
        void insert(int &x,int h,int t,int pos)
        {
            if (!x) x=++num;
            if (h==t)
            {
                v1[x]=pos; return;
            }
            if (pos<=mid) insert(ls[x],h,mid,pos);
            else insert(rs[x],mid+1,t,pos);
            updata(x);
        }
    }S; 
    void dfs(int x)
    {
        for (rint u=head[x];u;u=e[u].a)
        {
            int v=e[u].b;
            dfs(v);
            rt[x]=S.merge(rt[x],rt[v]);
        }
        if (pos[x]) S.insert(rt[x],1,n,pos[x]);
    }
    int x,y;
    IL bool pd(int now)
    {
        int kk1=S.query2(rt[now],1,n,x,y);
          if (kk1-T1.len[T1.fa[now]]+1<x) return 1; else return 0;
    }
    int main()
    {
      ios::sync_with_stdio(false);
      cin>>s;
      int l=strlen(s);
      rep(i,1,l)
      { 
        T1.extend(s[i-1]-'a');
        pos[T1.lst]=pos[T1.node]=i;
      }
      n=T1.node;
     // cerr<<T1.node<<endl;
      rep(i,2,n) arr(T1.fa[i],i); 
      dfs(1);
    //  cerr<<S.num<<endl;
      int k; cin>>k;
      rep(i,1,k)
      {
        cin>>s; cin>>x>>y;
        l=strlen(s);
        int now=1,cnt=0;
        rep(i,1,l)
        {
          T2.extend(s[i-1]-'a');
          while  (now&&(!T1.ch[now][s[i-1]-'a']||pd(T1.ch[now][s[i-1]-'a']))) now=T1.fa[now];      
          int kk1=S.query2(rt[now],1,n,x,y);
          cnt=MIN(cnt,MIN(kk1-x+1,T1.len[now]));
          if (now)
          {
            now=T1.ch[now][s[i-1]-'a'];
            int kk1=S.query2(rt[now],1,n,x,y);
            cnt=MIN(cnt+1,MIN(kk1-x+1,T1.len[now]));
            T2.jl[T2.lst]=cnt; 
          } else now=1,T2.jl[T2.lst]=0,cnt=0;
          T2.jl[T2.node]=T2.jl[T2.lst];
        }
        ll ans=0;
        rep(i,1,T2.node) 
          ans+=MAX(0,T2.len[i]-MAX(T2.len[T2.fa[i]],T2.jl[i]));
        cout<<ans<<endl;
        rep(i,1,T2.node) T2.size[i]=T2.fa[i]=T2.len[i]=T2.jl[i]=0;
        rep(i,1,T2.node) me(T2.ch[i]); T2.node=T2.lst=1;
      }
      return 0;
    }
  • 相关阅读:
    宁波工程学院2020新生校赛C
    宁波工程学院2020新生校赛B
    宁波工程学院2020新生校赛A -恭喜小梁成为了宝可梦训练家~(水题)
    POJ 1611
    牛客算法周周练11E
    牛客算法周周练11C
    牛客算法周周练11A
    CodeForces 1176C
    CodeForces 445B
    UVALive 3027
  • 原文地址:https://www.cnblogs.com/yinwuxiao/p/9437811.html
Copyright © 2011-2022 走看看