zoukankan      html  css  js  c++  java
  • 【BZOJ2754】[SCOI2012] 喵星球上的点名(后缀数组+莫队)

    点此看题面

    大致题意: 每个人的名字由姓和名构成,如果某次点名点到的字符串是某人姓或名的一个子串,则这个人就被点到了。求每次点名被点到的人的个数及每个人被点到的总次数。

    后缀数组+莫队

    这道题做法很多,可以用各种神仙自动机乱搞,也可以用后缀数组+莫队一起做。

    像我这么弱,什么自动机都不会,自然选择后面一种方法啦。

    后缀排序预处理

    首先我们要用一个后缀数组题常用的技巧,即将所有人的姓和名全部拼在一起,同时用(p)数组存储每个字符是属于哪一个人的。

    然后便是一遍后缀排序,求出(SA)数组。

    莫队

    然后,我们要考虑如何莫队

    考虑对于给定的字符串,它所对应的后缀在(SA)数组中肯定是一段连续的区间。

    则不难想到每次在(SA)数组中二分出合法的后缀。

    然后在(SA)数组上莫队即可。

    • 对于求每次被点到名的人的个数,可以开一个变量,更新有多少个后缀出现。
    • 对于每个人被点到的总次数,假设当前还剩下(k)个询问,则对于增加操作,我们将(tot_i+=k),反之,将(tot_i-=k),即可求出答案。

    具体实现详见代码。

    代码

    #include<bits/stdc++.h>
    #define N 50000
    #define M 100000
    #define Len 200000
    #define MAX 10000
    using namespace std;
    int n,len,query_tot,s[Len+5],p[Len+5];
    class Class_FIO
    {
        private:
            #define Fsize 100000
            #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
            #define pc(ch) (void)(FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
            int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize];
        public:
            Class_FIO() {A=B=Fin;}
            inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));}
            inline void write(int x) {if(!x) return pc('0');while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
            inline void writec(char x) {pc(x);}
            inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;}
    }F;
    class Class_CaptainMotao//莫队
    {
        private:
        	#define Add(x) (!cnt[p[t=x]]++&&(++res,tot[p[t]]+=query_cnt-i+1))//增加一个元素
        	#define Del(x) (!--cnt[p[t=x]]&&(--res,tot[p[t]]-=query_cnt-i+1))//删除一个元素
            int block_size,ans[M+5],tot[N+5],cnt[N+5];//ans存储询问答案,tot存储每个人被点到的次数
            struct Query
            {
            	int l,r,pos,bl;
            	Query(int x=0,int y=0,int p=0,int b=0):l(x),r(y),pos(p),bl(b){}
            	inline friend bool operator < (Query x,Query y) {return x.bl^y.bl?x.bl<y.bl:(x.bl&1?x.r<y.r:x.r>y.r);}
            }q[M+5];
            class Class_SuffixArray//后缀数组
            {
            	private:
            		int rk[Len+5],pos[Len+5],tot[Len+5];
            		inline void RadixSort(int S)
            		{
            			register int i;
            			for(i=0;i<=S;++i) tot[i]=0;
            			for(i=1;i<=len;++i) ++tot[rk[i]];
            			for(i=1;i<=S;++i) tot[i]+=tot[i-1];
            			for(i=len;i;--i) SA[tot[rk[pos[i]]]--]=pos[i];
                    }
            	public:
            		int SA[Len+5];
            		inline void Init(int len,int *s)
            		{
            			register int i,k,Size=Len,cnt=0;
            			for(i=1;i<=len;++i) rk[pos[i]=i]=s[i];
            			for(RadixSort(Size),k=1;cnt<len;k<<=1)
            			{
            				for(Size=cnt,cnt=0,i=1;i<=k;++i) pos[++cnt]=len-k+i;
            				for(i=1;i<=len;++i) SA[i]>k&&(pos[++cnt]=SA[i]-k);
            				for(RadixSort(Size),i=1;i<=len;++i) pos[i]=rk[i];
            				for(rk[SA[1]]=cnt=1,i=2;i<=len;++i) rk[SA[i]]=(pos[SA[i-1]]^pos[SA[i]]||pos[SA[i-1]+k]^pos[SA[i]+k])?++cnt:cnt;
                        }
                    }
            }S;
        public:
            inline void Solve()
            {
                register int i,j,query_cnt=0,x,y,l,r;
                for(S.Init(len,s),block_size=sqrt(len),i=1;i<=query_tot;++i)
                {
                    for(l=1,r=len,F.read(x),j=1;j<=x;++j)//每次在SA数组中二分出合法区间
                    {
                        register int tl=l,tr=r,mid=tl+tr>>1;
                        for(F.read(y);tl<=tr;mid=tl+tr>>1) 
                        s[S.SA[mid]+j-1]<y?tl=mid+1:tr=mid-1;
                        for(mid=(l=tl)+(tr=r)>>1;tl<=tr;mid=tl+tr>>1) 
                        s[S.SA[mid]+j-1]<=y?tl=mid+1:tr=mid-1;
                        r=tr;
                    }
                    l<=r&&(q[++query_cnt]=Query(l,r,i,(l-1)/block_size+1),0);//如果区间左边界小于右边界,则增加该询问
                }
                register int t,L=1,R=0,res=0;
                for(sort(q+1,q+query_cnt+1),i=1;i<=query_cnt;++i)//莫队处理询问
                {
                    while(R<q[i].r) Add(S.SA[++R]);while(L>q[i].l) Add(S.SA[--L]);while(R>q[i].r) Del(S.SA[R--]);while(L<q[i].l) Del(S.SA[L++]);
                    ans[q[i].pos]=res;
                }
                for(i=1;i<=query_tot;++i) F.write(ans[i]),F.writec('
    ');//输出答案
                for(i=1;i<=n;++i) F.write(tot[i]),F.writec(' ');//输出答案
            }
    }C;
    int main()
    {
        register int i,j,x,cnt=0;
        for(F.read(n),F.read(query_tot),i=1;i<=n;++i) 
        {
            for(++cnt,F.read(x),j=1;j<=x;++j) F.read(s[++len]),p[len]=cnt;s[++len]=MAX+(cnt<<1)-1;//读入拼接字符串
            for(F.read(x),j=1;j<=x;++j) F.read(s[++len]),p[len]=cnt;s[++len]=MAX+(cnt<<1);//读入拼接字符串
        }
        return C.Solve(),F.clear(),0;
    }
    
  • 相关阅读:
    装饰着模式
    观察者模式
    策略模式
    nginx配置图片防盗链
    nginx配置文件详解( 看着好长,其实不长,看了就知道了,精心整理,有些配置也是没用到呢 )
    php引用计数的基本知识
    PHP运行模式
    CURL常用命令--update20151015
    memcache相同主域名下的session共享
    memcached命令行操作详解,命令选项的详细解释
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2754.html
Copyright © 2011-2022 走看看