1. 程式人生 > >Luogu P2336 [SCOI2012]喵星球上的點名

Luogu P2336 [SCOI2012]喵星球上的點名

一道比較不錯字串好題,貌似有很多做法可以艹過去。

比較主流的有兩大類,一種是用各種自動機AC自動機暴力搞或者是神仙的字尾自動機做,相比之下對於我來說不是很會。

另一種就是用字尾陣列預處理一下,然後可以用各種資料結構(如樹狀陣列)來維護,但是由於我比較菜而且資料範圍不是很大所以我們可以用莫隊來暴力的搞。

下面我們主要分析一下SA+莫隊的演算法。

首先我們考慮將所有貓的姓和名拼接在一起(注意中間還是要用特殊字元連線一下),並記錄每一個位置上的字元是哪隻貓的,記為\(id_i\)

然後考慮對於詢問,由於字尾陣列的\(sa\)陣列表示的是排名為\(i\)的字尾的位置,因此我們可以根據排名二分

出每一個詢問在\(sa\)陣列上對應的區間

再回頭看這個問題,先考慮算詢問的答案,首先轉化為區間之後若左端點大於右端點那麼這個串顯然是找不到的,那麼可以直接跳過。

如果是合法的區間,那麼我們相當於統計這段區間中的所有\(sa_i\)\(id\)的種類數(因為每隻貓出現多次也只統計一次)

這個就直接開一個用莫隊算一下即可。

然後是考慮每隻貓的答案,這個我們莫隊更新區間的時候判斷一下,如果這隻貓是第一次出現那麼先將答案加上最大的可能出現次數,然後在刪除的時候減掉即可。

時間複雜度\(O(n\log n+m\sqrt n)\),可以輕鬆跑過。

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<iostream>
#include<algorithm>
#define RI register int
using namespace std;
const int N=50005,M=100005;
int blk[M+(N<<1)]; struct ques
{
    int l,r,id;
    inline friend bool operator <(ques A,ques B)
    {
        return blk[A.l]^blk[B.l]?blk[A.l]<blk[B.l]:(blk[A.l]&1?A.r<B.r:A.r>B.r);
    }
}q[M]; int n,m,len,tot,a[M+(N<<1)],sa[M+(N<<1)],x,size,ans[M],id[M+(N<<1)],cnt,t[N],bkt[N],lim=10000,L,R,ret;
class FileInputOutput
{
    private:
        #define S 1<<21
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
        #define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
        char Fin[S],Fout[S],*A,*B; int Ftop,pt[15];
    public:
        inline void read(int &x)
        {
            x=0; char ch; while (!isdigit(ch=tc()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
        }
        inline void write(int x,char ch)
        {
            if (!x) return (void)(pc('0'),pc(ch)); RI ptop=0;
            while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc(ch);
        }
        inline void Fend(void)
        {
            fwrite(Fout,1,Ftop,stdout);
        }
        #undef S
        #undef tc
        #undef pc
}F;
class Suffix_Array
{
    private:
        int rk[M+(N<<1)],t[M+(N<<1)],cnt[M+(N<<1)],size;
        inline void Radix_sort(int n)
        {
            RI i; for (i=0;i<=size;++i) cnt[i]=0;
            for (i=1;i<=n;++i) ++cnt[rk[i]];
            for (i=1;i<=size;++i) cnt[i]+=cnt[i-1];
            for (i=n;i;--i) sa[cnt[rk[t[i]]]--]=t[i];
        }
    public:
        inline void build(int *a,int n)
        {
            RI i; size=a[n]; for (i=1;i<=n;++i) rk[i]=a[i],t[i]=i;
            Radix_sort(n); for (RI p=0,w=1;p<n;size=p,w<<=1)
            {
                for (p=0,i=n-w+1;i<=n;++i) t[++p]=i;
                for (i=1;i<=n;++i) if (sa[i]>w) t[++p]=sa[i]-w;
                Radix_sort(n); swap(rk,t); rk[sa[1]]=p=1;
                for (i=2;i<=n;++i) rk[sa[i]]=(t[sa[i-1]]==t[sa[i]]&&t[sa[i-1]+w]==t[sa[i]+w])?p:++p;
            }
        }
}SA;
inline void add(int x,int cur)
{
    if (++bkt[id[x]]==1) ++ret,t[id[x]]+=cnt-cur+1;
}
inline void del(int x,int cur)
{
    if (--bkt[id[x]]==0) --ret,t[id[x]]-=cnt-cur+1;
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    RI i,j; for (F.read(n),F.read(m),i=1;i<=n;++i) for (RI k=0;k<=1;++k)
    {
        for (F.read(len),j=1;j<=len;++j) F.read(a[++tot]),id[tot]=i; a[++tot]=++lim;
    }
    for (size=(int)sqrt(tot),i=1;i<=tot;++i) blk[i]=(i-1)/size+1;
    for (SA.build(a,tot),i=1;i<=m;++i)
    {
        for (F.read(len),L=j=1,R=tot;j<=len;++j)
        {
            F.read(x); int l=L,r=R,mid; while (l<=r)
            if (a[sa[mid=l+r>>1]+j-1]<x) l=mid+1; else r=mid-1;
            int temp=l; l=L; r=R; while (l<=r)
            if (a[sa[mid=l+r>>1]+j-1]<=x) l=mid+1; else r=mid-1;
            L=temp; R=r;
        }
        if (L<=R) q[++cnt]=(ques){L,R,i};
    }
    for (sort(q+1,q+cnt+1),i=L=1,R=0;i<=cnt;++i)
    {
        while (L>q[i].l) add(sa[--L],i); while (R<q[i].r) add(sa[++R],i);
        while (L<q[i].l) del(sa[L++],i); while (R>q[i].r) del(sa[R--],i);
        ans[q[i].id]=ret;
    }
    for (i=1;i<=m;++i) F.write(ans[i],'\n');
    for (i=1;i<=n;++i) F.write(t[i],' ');
    return F.Fend(),0;
}