洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
题面
题解
字符串就硬是要和数据结构结合在一起么……\(loj\)上\(rk1\)好像码了\(10k\)的样子……
我们设\(L=r-l+1\)
首先可以发现对于\(T\)串一定是从左到右,能取就取是最优的
我们先用后缀自动机\(+\)线段树合并求出自动机上每一个节点的\(endpos\)集合。如果\(L\)较大的时候,我们考虑二分找到第一个端点,再找下一个……这样在线段树上找的总次数是\({n\over L}\),复杂度为\(O({n\over L}\log n)\)
但是\(L\)较小的时候我们显然不能这样乱蹦,那么我们对于每一个位置,维护一个\(f[L][i]\),表示一个长度为\(L\)的以\(i\)位置结尾的子串,下一个与它相同的位置在哪儿。这个对于所有\(L\)相同的询问显然可以统一处理。那么对于一个询问,只要知道它往后跳了几步,跳的这几步的位置的\(i\)之和就可以了,倍增即可
话说\(unordered\_map\)的头文件在\(c++11\)里居然会\(CE\)么……
//minamoto
#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define R register
#define ll long long
#define ull unsigned long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
using namespace std::tr1;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
int read(char *s){
R int len=0;R char ch;while(((ch=getc())>'z'||ch<'a'));
for(s[++len]=ch;(ch=getc())>='a'&&ch<='z';s[++len]=ch);
return s[len+1]='\0',len;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
while(z[++Z]=x%10+48,x/=10);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=2e5+5,M=N*20+5,Base=131;
struct node{
int s,t,l,r,id,L;
inline void init(R int Id){s=read(),t=read(),l=read(),r=read(),id=Id,L=r-l+1;}
inline bool operator <(const node &b)const{return L<b.L;}
}q[N];
char s[N],t[N];
int ch[N][26],fa[N],l[N],rt[N],f[N][21],F[N][21],G[N][21],lc[M],rc[M],sz[M];ll ans[N];
int c[N],a[N],id[N],mx[N],poi[N],Log[N],cl[N],zz[N];ull bin[N],h[N];
int las=1,cnt=1,tot,n,k,T;unordered_map<ull,int>mp;
inline ull gh(R int l,R int r){return h[r]-h[l-1]*bin[r-l+1];}
void ins(int c){
int p=las,np=las=++cnt;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(l[q]==l[p]+1)fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],4*26);
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
}
void upd(int &p,int l,int r,int x){
if(!p)p=++tot;++sz[p];if(l==r)return;
int mid=(l+r)>>1;
x<=mid?upd(lc[p],l,mid,x):upd(rc[p],mid+1,r,x);
}
int merge(int x,int y){
if(!x||!y)return x|y;
int p=++tot;
lc[p]=merge(lc[x],lc[y]),rc[p]=merge(rc[x],rc[y]);
sz[p]=sz[lc[p]]+sz[rc[p]];return p;
}
int find(int p,int l,int r){
if(l==r)return l;
int mid=(l+r)>>1;
return sz[lc[p]]?find(lc[p],l,mid):find(rc[p],mid+1,r);
}
int query(int p,int l,int r,int ql,int qr){
if(!sz[p])return -1;if(ql<=l&&qr>=r)return find(p,l,r);
int mid=(l+r)>>1,res=-1;
if(ql<=mid&&(res=query(lc[p],l,mid,ql,qr))!=-1)return res;
if(qr>mid&&(res=query(rc[p],mid+1,r,ql,qr))!=-1)return res;
return -1;
}
void solve_min(int p,int l,int r,int L,int id){
ll c=0,d=0;int x=query(rt[p],1,n,l,r);
if(x==-1)return ans[id]=0,void();
fd(i,zz[x],0)if(F[x][i]&&F[x][i]<=r)c+=(1<<i),d+=G[x][i],x=F[x][i];
++c,d+=x,ans[id]=c*k-d+(L-1)*c;
}
void solve_max(int p,int l,int r,int L,int id){
ll c=0,d=0;int x;
while(l<=r){
x=query(rt[p],1,n,l,r);
if(x==-1)break;
++c,d+=x,l=x+L;
}
ans[id]=c*k-d+(L-1)*c;
}
void init(){
n=read(),k=read(),read(s),read(t);
fp(i,1,n)ins(s[i]-'a'),upd(rt[las],1,n,i);
fp(i,1,cnt)f[i][0]=fa[i],++c[l[i]];
for(R int j=1;(1<<j)<=cnt;++j)fp(i,1,cnt)f[i][j]=f[f[i][j-1]][j-1];
fp(i,1,cnt)c[i]+=c[i-1];
fd(i,cnt,1)id[c[l[i]]--]=i;
for(R int i=cnt,p=id[i];i>1;--i,p=id[i])
rt[fa[p]]=merge(rt[fa[p]],rt[p]);
for(R int i=1,p=1,now=0;i<=n;++i){
R int c=t[i]-'a';
if(ch[p][c])p=ch[p][c],++now;
else{
for(;p&&!ch[p][c];p=fa[p]);
p?(now=l[p]+1,p=ch[p][c]):(p=1,now=0);
}
mx[i]=now,poi[i]=p;
}
bin[0]=1,Log[0]=-1;
fp(i,1,n)bin[i]=bin[i-1]*Base,h[i]=h[i-1]*Base+s[i]-'a'+1,Log[i]=Log[i>>1]+1;
}
void solve(){
T=read();fp(i,1,T)q[i].init(i);
sort(q+1,q+1+T);
int pos=1,s,t,l,r,id,p,L;
for(R int L=1;L<=50;++L)if(q[pos].L==L){
fd(i,n,L){
R ull tmp=gh(i-L+1,i);
if(mp.count(tmp)){
R int x=mp[tmp],sz=Log[cl[x]+1];
zz[i]=sz,F[i][0]=x,G[i][0]=i,cl[i]=cl[x]+1;
fp(j,1,sz){
x=F[i][j-1];
F[i][j]=F[x][j-1],G[i][j]=G[i][j-1]+G[x][j-1];
}
}else cl[i]=0;
if(i+L-1<=n)mp[gh(i,i+L-1)]=i+L-1;
}
unordered_map<ull,int>().swap(mp);
for(;pos<=T&&q[pos].L==L;++pos){
s=q[pos].s,t=q[pos].t,l=q[pos].l,r=q[pos].r,id=q[pos].id;
if(mx[r]<L){ans[id]=0;continue;}
p=poi[r];
fd(j,Log[::l[p]],0)if(::l[f[p][j]]>=L)p=f[p][j];
solve_min(p,s+L-1,t,L,id);
}
fd(i,n,L)fp(j,0,zz[i])F[i][j]=G[i][j]=0;
}
for(;pos<=T;++pos){
s=q[pos].s,t=q[pos].t,l=q[pos].l,r=q[pos].r,id=q[pos].id,L=r-l+1;
if(mx[r]<L){ans[id]=0;continue;}
p=poi[r];
fd(j,Log[::l[p]],0)if(::l[f[p][j]]>=L)p=f[p][j];
solve_max(p,s+L-1,t,L,id);
}
fp(i,1,T)print(ans[i]);
}
int main(){
// freopen("testdata.in","r",stdin);
init(),solve();
return Ot(),0;
}
洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)的更多相关文章
- 洛谷P2408 不同字串个数 [后缀数组]
题目传送门 不同字串个数 题目背景 因为NOI被虐傻了,蒟蒻的YJQ准备来学习一下字符串,于是它碰到了这样一道题: 题目描述 给你一个长为N的字符串,求不同的子串的个数 我们定义两个子串不同,当且仅当 ...
- 【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)
[BZOJ5304][HAOI2018]字串覆盖(后缀数组,主席树,倍增) 题面 BZOJ 洛谷 题解 贪心的想法是从左往右,能选就选.这个显然是正确的. 题目的数据范围很好的说明了要对于询问分开进行 ...
- 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)
题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- BZOJ1396: 识别子串(后缀自动机 线段树)
题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
随机推荐
- 6.solr学习速成之multicore查询
查询关联多个core 再新建一个core 向每个core添加索引,修改 final static String SOLR_URL = "http://localhost:8080/solr/ ...
- 循序渐进Python3(十一) --4-- web之jQuery
jQuery jQuery是一个快速.简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架).jQuery设 ...
- Hadoop Serialization(third edition)hadoop序列化详解(最新版) (1)
初学java的人肯定对java序列化记忆犹新.最开始很多人并不会一下子理解序列化的意义所在.这样子是因为很多人还是对java最底层的特性不是特别理解,当你经验丰富,对java理解更加深刻之后,你就会发 ...
- Objective-C入门 简介Cocoa框架
Cocoa Framework简称Cocoa,它是Mac OS X上的快速应用程序开发(RAD, Rapid Application Development)框架,一个高度面向对象的(Object O ...
- linux进行Java开发环境的部署
一.前言: 今天正式向linux开发进攻了,其中遇到一些问题简单的记录一下,为之后的再次部署提供方便. 二.linux的Java8安装的两种方法: 1.源安装很简单,一个命令搞定. sudo apt- ...
- java基础之io流总结四:字符流读写
字符流读写只适用于字符文件. 基本字符流(转换流)读写文件 转换流本身是字符流,但是实例化的时候传进去的是一个字节流,所以叫做转换流 InputStreamReader isr = new Input ...
- tomcat在linux服务器上部署应用
连接服务器 服务器地址:xxx.xxx.xxx.xxx 用户名:xxxx 密码:xxxx 进入到服务器中的tomcat路径,关闭服务器,例如 路径:/opt/wzgcyth/apache-tomcat ...
- instanceof php
instdnceof php5 的一个新成员 功能: 使用这个关键字可以确定一个对象是否是类的实例,是否是累的子类 ,还是实现了某个特定的接口. <?php class A{} class B ...
- 面试题:彻底理解ThreadLocal 索引的利弊 背1
.索引利弊 --整理 1.索引的好处 a.提高数据检索的效率,降低检索过程中必须要读取得数据量,降低数据库IO成本. b.降低数据库的排序成本.因为索引就是对字段数据进行排序后存储的,如果待排序的 ...
- linux的deamon后台运行
有的时候需要将程序一直跑在后台,比如一些服务类代码,或者一些监控类代码.使用deamon是正确的一种思路. 以前我们在看<unix环境高级编程>的时候,有专门的整章详细介绍如何编写一个后台 ...