• [SCOI2012]喵星球上的点名


    Link

    Description

     一共(n)个人,每个人有两个名字串。再给定(m)个点名串。询问每个点名串是多少人名字串的子串。第二问询问对于每个人,一共有多少点名串是其两名字串(满足一个串即可)的子串。

    Solution

    SA+莫队。

    结论:串(T)是串(S)的子串,应当满足串(S)有某个后缀和串(T)(lcp)(|T|)

    我们先把所有串都接在一起,中间用分隔符隔开。然后求一遍SA。

    因为SA上每个点向两边扩展,两后缀的lcp就越短,所以我们可以在SA上从每个点名串对应的位置(即串首代表的后缀在SA上的位置)出发,二分出它的合法区间。

    第一问转化为:求序列上一段区间的颜色个数。莫队裸题。

    对于第二问,考虑差分:当某个颜色数量减少0时,就把他的答案减去剩余询问个数;当当某个颜色数量从0变成1时,就加上剩余询问个数。

    tips:

    • 连接两串的分隔符,分隔符应两两不同

       假设有两后缀

      aa#aa!aa...
      aa!aa...
      

       如果分隔符相同:

      aa#aa...
      aa#aa#aa... 
      

       就会导致lcp判长了。

    • 莫队的时候先增后减。就是说移动指针先考虑让数量增加的两种。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    inline int read(){//be careful for long long!
        register int x=0,f=1;register char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
        while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
        return f?x:-x;
    }
    
    const int N=1e5+10;
    int tp,n,m,s[N<<2],col[N<<2],pos[N],len[N],bel[N<<2],cnt[N],ans[N],res,lim=1e4;
    struct s_query{int l,r,ans,id;}q[N];
    inline bool operator <(const s_query &a,const s_query &b){
        return bel[a.l]==bel[b.l]?(bel[a.l]&1?a.r<b.r:a.r>b.r):bel[a.l]<bel[b.l];
    }
    inline bool cmp_id(const s_query &a,const s_query &b){return a.id<b.id;}
    
    int sa[N<<2],rnk[N<<2],height[N<<2],tmp[N<<2],A[N<<2],B[N<<2],cntA[N<<2],cntB[N<<2],lg[N<<2],st[N<<2][19];
    inline void SA(){
        for(int i=1;i<=tp;++i)++cntA[s[i]];
        for(int i=1;i<=lim;++i)cntA[i]+=cntA[i-1];
        for(int i=tp;i;--i)sa[cntA[s[i]]--]=i;
        rnk[sa[1]]=1;
        for(int i=2;i<=tp;++i){
    	rnk[sa[i]]=rnk[sa[i-1]];
    	if(s[sa[i]]^s[sa[i-1]])++rnk[sa[i]];
        }
        for(int len=1;rnk[sa[tp]]<tp;len<<=1){
    	memset(cntA,0,sizeof(int)*(tp+1));memset(cntB,0,sizeof(int)*(tp+1));
    	for(int i=1;i<=tp;++i){
    	    ++cntA[A[i]=rnk[i]];
    	    ++cntB[B[i]=i+len>tp?0:rnk[i+len]];
    	}
    	for(int i=1;i<=tp;++i)cntB[i]+=cntB[i-1];
    	for(int i=tp;i;--i)tmp[cntB[B[i]]--]=i;
    	for(int i=1;i<=tp;++i)cntA[i]+=cntA[i-1];
    	for(int i=tp;i;--i)sa[cntA[A[tmp[i]]]--]=tmp[i];
    	rnk[sa[1]]=1;
    	for(int i=2;i<=tp;++i){
    	    rnk[sa[i]]=rnk[sa[i-1]];
    	    if(A[sa[i]]^A[sa[i-1]]||B[sa[i]]^B[sa[i-1]])++rnk[sa[i]];
    	}
        }
        for(int i=1,j=0;i<=tp;++i){
    	if(j)--j;
    	while(s[i+j]==s[sa[rnk[i]-1]+j])++j;
    	height[rnk[i]]=j;
        }
        for(int i=2;i<=tp;++i)lg[i]=lg[i>>1]+1;
        for(int i=1;i<=tp;++i)st[i][0]=height[i];
        for(int j=1;j<=lg[tp];++j)
    	for(int i=1;i+(1<<j)-1<=tp;++i)
    	    st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    }
    inline int Query(int x,int y){
        int k=lg[y-x+1];
        return min(st[x][k],st[y-(1<<k)+1][k]);
    }
    
    inline void Add(int pos,int t){
        int c=col[sa[pos]];
        if(!cnt[c]&&c)++res,ans[c]+=m-t+1;
        ++cnt[c];
    }
    inline void Del(int pos,int t){
        int c=col[sa[pos]];
        --cnt[c];
        if(!cnt[c]&&c)--res,ans[c]-=m-t+1;
    }
    
    int main(){
    //    freopen("name4.in","r",stdin);freopen("out.out","w",stdout);
        n=read(),m=read();
        for(int t=1;t<=n;++t){
    	s[++tp]=++lim;
    	for(int i=1,l=read();i<=l;++i)s[++tp]=read(),col[tp]=t;
    	s[++tp]=++lim;
    	for(int i=1,l=read();i<=l;++i)s[++tp]=read(),col[tp]=t;
        }
        for(int t=1;t<=m;++t){
    	s[++tp]=++lim;pos[t]=tp+1;len[t]=read();
    	for(int i=1;i<=len[t];++i)s[++tp]=read();
        }
        SA();
        for(int t=1;t<=m;++t){
    	int l,r;q[t]=(s_query){rnk[pos[t]],rnk[pos[t]],0,t};
    	l=1,r=rnk[pos[t]];
    	while(l<=r){
    	    int mid=(l+r)>>1;
    	    if(Query(mid+1,rnk[pos[t]])>=len[t])q[t].l=mid,r=mid-1;
    	    else l=mid+1;
    	}
    	l=rnk[pos[t]],r=tp;
    	while(l<=r){
    	    int mid=(l+r)>>1;
    	    if(Query(rnk[pos[t]]+1,mid)>=len[t])q[t].r=mid,l=mid+1;
    	    else r=mid-1;
    	}
        }
        for(int i=1,block=sqrt(tp);i<=tp;++i)bel[i]=(i-1)/block+1;
        sort(q+1,q+m+1);
        for(int t=1,l=1,r=0;t<=m;++t){
    	while(l>q[t].l)Add(--l,t);
    	while(r<q[t].r)Add(++r,t);
    	while(l<q[t].l)Del(l++,t);
    	while(r>q[t].r)Del(r--,t);
    	q[t].ans=res;
        }
        sort(q+1,q+m+1,cmp_id);
        for(int t=1;t<=m;++t)printf("%d
    ",q[t].ans);
        for(int i=1;i<=n;++i)printf("%d ",ans[i]);
        puts("");
        return 0;
    }
    
  • 相关阅读:
    [文摘20070930]激励员工20种非经济手段
    [文摘20071010]绿领
    新增及删除Grid行
    SqlAnyWhere相关示例SQL语句
    [文摘20071008]全国软考相关计算机技术与软件专业资格(水平)考试
    愉悦的时候
    [文摘20070924]导致事业不成功的心理致命伤
    快乐的生活
    给网络创业者的十大建议
    [转]winForm:DataGridView的一些技巧
  • 原文地址:https://www.cnblogs.com/fruitea/p/12081523.html
Copyright © 2020-2023  润新知