• 洛谷 P2336 [SCOI2012]喵星球上的点名(后缀数组+莫队)


    传送门

    题意

    (2 imes N)个主串,(2) 个一组,和 (M) 个模式串
    计算包含每个模式串的主串组的个数,每个主串包含多少个模式串

    思路

    先将所有串拼起来,用特殊符号间隔,并且标记每个属于主串的后缀
    走一遍后缀数组之后,对于每个询问,找到对应的区间
    然后把这些区间来做莫队,
    计算包含某模式串的主串数就是莫队的普通操作,
    计算每个主串包含多少模式串,可以在加入时直接对该主串答案加上最多可能的答案,然后再删去的时候减去多加的,具体细节看代码

    代码

    #include <iostream>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <math.h>
    #include <algorithm>
    #include <set>
    using namespace std;
    const int MAXN=1e6+10;
    int N,M,q[MAXN],len[MAXN],sz;
    int n,m,s[3*MAXN],id[3*MAXN],st[3*MAXN][22];
    int sa[3*MAXN],rk[6*MAXN],tp[6*MAXN],c[10010],ht[3*MAXN];
    int vis[MAXN],ans[MAXN],cnt=0,tot[MAXN];
    struct Ques{
    	int l,r,id;
    }ques[MAXN];
    bool cmp(Ques a,Ques b){
    	return a.l/sz<b.l/sz||a.l/sz==b.l/sz&&a.r<b.r;
    }
    
    void getsa(){
    	m=10002;
    	for(int i=1;i<=n;i++) c[rk[i]=s[i]]++;
    	for(int i=1;i<=m;i++) c[i]+=c[i-1];
    	for(int i=n;i>=1;i--) sa[c[rk[i]]--]=i;
    	for(int w=1,p=0;p<n;w<<=1,m=p){
    		p=0;
    		for(int i=n-w+1;i<=n;i++) tp[++p]=i;
    		for(int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
    		for(int i=1;i<=m;i++) c[i]=0;
    		for(int i=1;i<=n;i++) c[rk[i]]++;
    		for(int i=1;i<=m;i++) c[i]+=c[i-1];
    		for(int i=n;i>=1;i--) sa[c[rk[tp[i]]]--]=tp[i];
    		swap(rk,tp);
    		rk[sa[1]]=p=1;
    		for(int i=2;i<=n;i++)
    			rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])?p:++p;
    	}
    	int k=0;
    	for(int i=1;i<=n;i++){
    		if(rk[i]==1) continue;
    		if(k) k--;
    		int j=sa[rk[i]-1];
    		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
    		ht[rk[i]]=k;
    	}
    }
    
    int lcp(int x,int y){
    	int l=rk[x],r=rk[y];
    	if(l>r) swap(l,r);
    	if(l==r) return n-x+1;
    	int t=log2(r-l);
    	return min(st[l+1][t],st[r-(1<<t)+1][t]);
    }
    
    void add(int pos,int now){
    	if(!id[pos]) return;
    	if(!vis[id[pos]]) cnt++,tot[id[pos]]+=M-now+1;
    	vis[id[pos]]++;
    }
    
    void del(int pos,int now){
    	if(!id[pos]) return;
    	vis[id[pos]]--;
    	if(!vis[id[pos]]) cnt--,tot[id[pos]]-=M-now+1; 
    }
    
    int main(){
    	scanf("%d%d",&N,&M);
    	sz=sqrt(N);
    	for(int i=1,l;i<=N;i++){
    		scanf("%d",&l);
    		for(int j=1;j<=l;j++) scanf("%d",&s[++n]),s[n]+=2,id[n]=i;
    		s[++n]=1;
    		scanf("%d",&l);
    		for(int j=1;j<=l;j++) scanf("%d",&s[++n]),s[n]+=2,id[n]=i;
    		s[++n]=1;
    	}
    	for(int i=1,l;i<=M;i++){
    		scanf("%d",&l);
    		len[i]=l;
    		q[i]=n+1;
    		for(int j=1;j<=l;j++) scanf("%d",&s[++n]),s[n]+=2;
    		s[++n]=1;
    	}
    	getsa();
    	for(int i=1;i<=n;i++) st[i][0]=ht[i];
    	for(int j=1;j<=20;j++)
    		for(int i=1;i<=n-(1<<j)+1;i++)
    			st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    	
    	for(int i=1,l,r;i<=M;i++){
    		l=1,r=rk[q[i]];
    		while(l<r){
    			int mid=(l+r)>>1;
    			if(lcp(sa[mid],q[i])>=len[i]) r=mid;
    			else l=mid+1;
    		}
    		ques[i].l=l;
    		l=rk[q[i]],r=n;
    		while(l<r){
    			int mid=(l+r+1)>>1;
    			if(lcp(sa[mid],q[i])>=len[i]) l=mid;
    			else r=mid-1;
    		}
    		ques[i].r=l;
    		ques[i].id=i;
    	}
    	sort(ques+1,ques+M+1,cmp);
    	for(int i=1,L=1,R=0;i<=n;i++){
    		while(R<ques[i].r) add(sa[++R],i);
    		while(R>ques[i].r) del(sa[R--],i);
    		while(L>ques[i].l) add(sa[--L],i);
    		while(L<ques[i].l) del(sa[L++],i);
    		ans[ques[i].id]=cnt;
    	}
    	for(int i=1;i<=M;i++) cout<<ans[i]<<endl;
    	for(int i=1;i<=N;i++) cout<<tot[i]<<" ";cout<<endl;
    	return 0;
    }
    
  • 相关阅读:
    图解JAVA对象的创建过程
    统计机器学习
    排序算法简介及其C实现
    linux中强大的screen命令
    C语言注释
    Hello hadoop——使用hadoop进行大规模数据的全局排序
    Hadoop Streaming框架使用(二)
    shell——tr的用法
    统计学习方法《文本分类(三)》
    hadoop 常存问题
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/11987677.html
Copyright © 2020-2023  润新知