P2336 [SCOI2012]喵星球上的点名 SA+树状数组

题意:

戳这里

分析:

两个询问分开考虑:

  1. 求每次有多少只 喵 被点了名

我们一看见子串多个模式串,第一反应就是广义SAM,可惜广义SAM做不了,因为第一个操作限制了每一个人每次只能答到一次,即每种类型的串只看做匹配一次

如果不考虑这个限制条件的话,那么我们可以建出一个广义SAM,或者用分隔符将字符串连接起来求SA数组,然后直接统计子串个数

那么我们考虑怎么消掉这个影响呢,我们发现以该询问串为子串的串在 (SAM) 上是 (parent) 树上的一颗子树,是 (SA) 数组上一段连续的区间,所以我们相当于求区间(子树的dfn序也是一段区间)内喵的种类数,这个操作很 HH的项链(不会请出门右转 P1972 )

  1. 求每只喵被点了多少次

我们沿着第一个问题的思路,每个询问串影响的是一段区间,而第二个问题等价于求每种喵被多少个区间覆盖到了,这个就很扫描线,我们为了保证每种串只会被每个颜色统计一次,我们就按照 HH的项链 的思想,每次只统计 ([lst[col[i]],i]) 这一段内有多少个查询区间,区间数可以通过树状数组维护差分实现

至此,此题的做法就完了,上面也分析过了,不论是 SAM 或者 SA 都能实现,但是由于 SAM 太麻烦,构建 parent 树后还要求 dfn 序,所以这里采用了 SA 的写法

tip:

其实 SA 的写法也不好写,因为 SA 需要左右分别二分才能找到对应区间,而 SAM 只需要跑匹配就能找到相应的子树了

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }

    const int maxn = 5e4+4;
    const int maxq = 1e5+5;
    const int maxm = 1e6+5;
    int n,m,qt;
    int lst[maxn],ans1[maxq],ans2[maxq],fir[maxm],len[maxm],a[maxm],bel[maxm];
    int sa[maxm],rk[maxm],oldrk[maxm],id[maxm],tmp[maxm],cnt[maxm],lg[maxm];
    int ht[maxm][20];
    
    struct query
    {
        int l,r,id;
        bool operator <(const query &b)const
        {
            return r<b.r;
        }
    }q[maxq];
    
    struct bit_tree
    {
        int c[maxm];
        void clear(){memset(c,0,sizeof(c));}
        int lowbit(int x){return x&(-x);}
        void update(int x,int v){for(int i=x;i<=n;i+=lowbit(i))c[i]+=v;}
        int query(int x){int res=0;for(int i=x;i;i-=lowbit(i))res+=c[i];return res;}
    }tr;

    bool check(int x,int y,int k)
    {
        return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];
    }

    void build()
    {
        int num=500000;
        for(int i=1;i<=n;i++) cnt[rk[i]=a[i]]++;
        for(int i=1;i<=num;i++) cnt[i]+=cnt[i-1];
        for(int i=n;i;i--) sa[cnt[rk[i]]--]=i;
        for(int t=1;t<=n;t<<=1)
        {
            int tot=0;
            for(int i=n-t+1;i<=n;i++) id[++tot]=i;
            for(int i=1;i<=n;i++) if(sa[i]>t) id[++tot]=sa[i]-t;
            tot=0;
            memset(cnt,0,sizeof(cnt));
            for(int i=1;i<=n;i++) cnt[tmp[i]=rk[id[i]]]++;
            for(int i=1;i<=num;i++) cnt[i]+=cnt[i-1];
            for(int i=n;i;i--) sa[cnt[tmp[i]]--]=id[i];
            memcpy(oldrk,rk,sizeof(rk));
            for(int i=1;i<=n;i++) rk[sa[i]]=check(sa[i-1],sa[i],t)?tot:++tot;
            num=tot;
        }
        for(int i=1,j=0;i<=n;i++)
        {
            if(j)j--;
            while(a[i+j]==a[sa[rk[i]-1]+j]) j++;
            ht[rk[i]][0]=j;
        }
        for(int j=1;j<=18;j++)
        {
            for(int i=1;i+(1<<j)-1<=n;i++)
            {
                ht[i][j]=min(ht[i][j-1],ht[i+(1<<(j-1))][j-1]);
            }
        }
    }
    
    void init(int now)
    {
        fir[now]=n+1;len[now]=read();
        for(int i=1;i<=len[now];i++) a[++n]=read(),bel[n]=now;
        a[++n]=now+10000;
    }

    int query(int l,int r)
    {
        l++;
        int t=lg[r-l+1];
        return min(ht[l][t],ht[r-(1<<t)+1][t]);
    }

    int findl(int id)
    {
        int l=1,r=rk[fir[id]]-1,mid,ans=rk[fir[id]];
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(query(mid,rk[fir[id]])<len[id])l=mid+1;
            else ans=mid,r=mid-1;
        }
        return ans;
    }

    int findr(int id)
    {
        int l=rk[fir[id]]+1,r=n,mid,ans=rk[fir[id]];
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(query(rk[fir[id]],mid)<len[id]) r=mid-1;
            else ans=mid,l=mid+1;
        }
        return ans;
    }
    
    void work()
    {
        lg[0]=-1;tr.clear();
        for(int i=1;i<=1000000;i++) lg[i]=lg[i>>1]+1;
        m=read();qt=read();
        for(int i=1;i<=m;i++) init(2*i-1),init(2*i);
        for(int i=1;i<=qt;i++) init(2*m+i);
        build();
        for(int i=1;i<=qt;i++) q[i].l=findl(2*m+i),q[i].r=findr(2*m+i),q[i].id=i;
        sort(q+1,q+qt+1);
        for(int i=1,j=1;i<=qt;i++)
        {
            while(j<=q[i].r)
            {
                if(bel[sa[j]]<=2*m)
                {
                    if(lst[(bel[sa[j]]+1)>>1]) tr.update(lst[(bel[sa[j]]+1)>>1],-1);
                    lst[(bel[sa[j]]+1)>>1]=j;
                    tr.update(j,1);
                }
                j++;
            }
            ans1[q[i].id]=tr.query(q[i].r)-tr.query(q[i].l-1);
        }
        for(int i=1;i<=m;i++) lst[i]=0;tr.clear();
        for(int i=1;i<=qt;i++) tr.update(q[i].l,1);
        for(int i=1,j=1;i<=n;i++)
        {
            while(j<=qt&&q[j].r<i) tr.update(q[j].l,-1),j++;
            if(bel[sa[i]]<=2*m)
            {
                ans2[(bel[sa[i]]+1)>>1]+=tr.query(i)-tr.query(lst[(bel[sa[i]]+1)>>1]);
                lst[(bel[sa[i]]+1)>>1]=i;
            }
        }
        for(int i=1;i<=qt;i++) printf("%d
",ans1[i]);
        for(int i=1;i<=m;i++) printf("%d ",ans2[i]);
    }

}

int main()
{
    zzc::work();
    return 0;
}
原文地址:https://www.cnblogs.com/youth518/p/14204449.html