zoukankan      html  css  js  c++  java
  • BZOJ4212 神牛的养成计划

    题目大意

    给定$n$个字符串$S$,$m$次询问,每次询问给定两个字符串$s_1,s_2$,求$n$个字符串中有多少个串满足$s_1$是其前缀且$s_2$是其后缀。

    $nleq 2000,mleq 10^5 sum |S|,sum (|s_1|+|s_2|) leq 2 imes 10^6$

    题解

    考虑正反建$Trie$树,原题就变为了在两棵子树中均出现的点有多少个。

    你当然可以用主席树加二维数点解决,其实也可以将每一个串按照正向$Trie$树$Dfs$序排序,然后再反向建可持久化$Trie$,每次询问就先找到正向$dfs$序要求的区间,接着在可持久化$Trie$的两个对应的根中沿着$s_2$的反串得出答案。

    复杂度为$O(nlog n+sum |S|+sum (|s_1|+|s_2|)+mlog n)$。

    所以其实$n$甚至可以出成$10^5$级。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 2220000
    #define N 2010
    using namespace std;
    namespace IO{
    	const int BS=(1<<19); int Top=0;
    	char OT[BS],*OS=OT,SS[20]; const char *fin=OT+BS-1;
    	void flush(){fwrite(OT,1,OS-OT,stdout);}
    	void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;}
    	void write(int x){
    		if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-');
    		while(x) SS[++Top]=x%10,x/=10;
    		while(Top) Putchar(SS[Top]+'0'),--Top;
    	}
    	int read(){
    		int nm=0,fh=1; char cw=getchar();
    		for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    		for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    		return nm*fh;
    	}
    }using namespace IO;
    char ch[M],*s[N],s1[M],s2[M]; int n,m,p[M][26],tg[N],len[N];
    int sz[M],dfn[M],node[N],tot,Rt,rt[N];
    int cnt,t[M][26],od[N],sum[M];
    int ins(int &x,char *k,int rem){
    	if(!x) x=++tot; if(!rem) return x;
    	ins(t[x][k[0]-'a'],k+1,rem-1);
    }
    void dfs(int x){
    	if(!x) return; dfn[x]=++cnt,sz[x]=1;
    	for(int i=0;i<26;i++) dfs(t[x][i]),sz[x]+=sz[t[x][i]];
    }
    bool cmp(int x,int y){return dfn[node[x]]<dfn[node[y]];}
    void ist(int &x,int pre,char *k,int rem){
    	sum[x=++cnt]=sum[pre]+1;
    	memcpy(p[x],p[pre],sizeof(p[x])); if(!rem) return;
    	ist(p[x][k[0]-'a'],p[pre][k[0]-'a'],k-1,rem-1);
    }
    int qry(int x,int pre,char *k,int rem){
    	if(sum[x]==sum[pre]) return 0; if(!rem) return sum[x]-sum[pre];
    	return qry(p[x][k[0]-'a'],p[pre][k[0]-'a'],k-1,rem-1);
    }
    void match(int &l,int &r,int x,char *k,int rem){
    	if(!rem){l=dfn[x],r=dfn[x]+sz[x]-1;return;}
    	match(l,r,t[x][k[0]-'a'],k+1,rem-1);
    }
    int fd(int tim){
    	int ls=1,rs=n,res=n+1,md;
    	while(ls<=rs){
    		md=((ls+rs)>>1);
    		if(dfn[node[od[md]]]>tim) rs=md-1;
    		else res=md,ls=md+1;
    	} return res;
    }
    int main(){
    	n=read(),s[0]=ch;
    	for(int i=1;i<=n;i++){
    		s[i]=s[i-1]+len[i-1],od[i]=i;
    		scanf("%s",s[i]),len[i]=strlen(s[i]);
    		node[i]=ins(Rt,s[i],len[i]);
    	} dfs(Rt),cnt=0,sort(od+1,od+n+1,cmp);
    	for(int i=1;i<=n;i++) ist(rt[i],rt[i-1],s[od[i]]+len[od[i]]-1,len[od[i]]);
    	for(int T=read(),ans=0,len1,len2,L,R;T;T--,write(ans),Putchar('
    ')){
    		scanf("%s%s",s1,s2),len1=strlen(s1),len2=strlen(s2);
    		for(int i=0;i<len1;i++) s1[i]=(s1[i]-'a'+ans)%26+'a';
    		for(int i=0;i<len2;i++) s2[i]=(s2[i]-'a'+ans)%26+'a';
    		match(L,R,Rt,s1,len1); if(!R){ans=0;continue;}
    		L=fd(L-1),R=fd(R),ans=qry(rt[R],rt[L],s2+len2-1,len2);
    	}flush();return 0;
    }
  • 相关阅读:
    Log4net 配置详解
    JS 浮点计算BUG
    EF 一对一,一对多,多对多 Flunt API 配置
    分享一个近期写的简单版的网页采集器
    Quartz.net Cron表达式
    客户端负载均衡—Ribbon初探
    服务注册与发现—Eureka初探
    第五坑:这颗语法糖不太甜(自动装箱拆箱)
    Redis学习笔记
    Java集合——HashMap
  • 原文地址:https://www.cnblogs.com/OYJason/p/9816097.html
Copyright © 2011-2022 走看看