链接大合集:

loj

uoj

luogu

bzoj

单纯地纪念一下写的第一份5K代码。。。/躺尸

因为ZJOI都不会所以只好写NOI的题了。。。

总之字符串题肯定一上来就拼个大字符串跑后缀数组啦!

(为了便于说明,放一下样例的sa)

 #sbape#sgepe
#sgepe
#smape#sbape#sgepe
amgepe#smape#sbape#sgepe
ape#sbape#sgepe
ape#sgepe
bamgepe#smape#sbape#sgepe
bape#sgepe
cbamgepe#smape#sbape#sgepe
e
e#sbape#sgepe
e#sgepe
e#smape#sbape#sgepe
epe
epe#smape#sbape#sgepe
gepe
gepe#smape#sbape#sgepe
mape#sbape#sgepe
mgepe#smape#sbape#sgepe
pe
pe#sbape#sgepe
pe#sgepe
pe#smape#sbape#sgepe
sbape#sgepe
scbamgepe#smape#sbape#sgepe
sgepe
smape#sbape#sgepe

首先,对于每个T,考虑求出其子串总数。对于T每个后缀[i...|T|],用st表求该后缀和它前面第一个T的后缀的lcp,则这个后缀的有效子串即为[i...j](j属于[i+lcp,|T|])。

(对于"sgepe"的后缀"epe",lcp(10,14)=1,因此,其有效子串为"ep","epe")

然后,我们再考虑去掉其有效子串中S[l...r]中的部分。

考虑64pts的部分分,即l=1,r=n。对于T的后缀[i...|T|],只要求出L=max{lcp( T[i...|T|] , S[j...|S|] )},我们就能算出每个后缀对答案的贡献。可以发现,这两个部分分别在区间上取min和max,即分别递增、递减,因此我们只要找到两个函数值最靠近的位置即可。

考虑100pts,我们可以离线做。按询问的l从大到小排序,每次在线段树上加入新的字符串使得线段树中的字符串为S[i...|S|](i>=l)。显然,i>r的字符串并不会对答案产生影响,于是我们就可以愉快地线段树上二分啦!(二分需要找到最近的满足递减函数小于递增函数的位置,在线段树上需要先上去再下来,总之写起来很恶心就对了。。。写了个人不人鬼不鬼的zkw线段树(好吧其实我并不怎么会zkw线段树。。。)总之这就是长达5k的原因。。。)

(啊,代码真丑。)

(不知道是不是因为大家都是sam而我是sa所以这么慢。。。)

(sa:所以自己常数大反而怪我咯?←_←)

 #include <bits/stdc++.h>
using namespace std; #define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,r,l) for(int i=r;i>=l;--i)
#define pb push_back
#define gc getchar()
#define pc putchar typedef pair<int,int> pii;
typedef long long ll; const int N=2e6+; int rd(){
int x=;char c=gc;
while(c<''||c>'') c=gc;
while(c>=''&&c<=''){
x=x*+c-'';
c=gc;
} return x;
} void prt(ll x){
if(x>) prt(x/);
pc(x%+'');
} //Q 查询字符串 l-r/r-l
int L,n,m,sa[N];
char s[N]; namespace Sa{
const int V=;
int ht[N],ar[][N],*rk,*tp,ct[N],mx,ln,st[V+][N],lg[N]; void get_sa(){
rk=ar[];tp=ar[]; rep(i,,n) ct[s[i]]++;
rep(i,,) ct[i]+=ct[i-];
rep(i,,n) sa[ct[s[i]]--]=i;
rep(i,,n){
if(s[sa[i]]!=s[sa[i-]]) ++mx;
rk[sa[i]]=mx;
} for(int k=;mx<n;k<<=){
ln=;
rep(i,n-k+,n) tp[++ln]=i;
rep(i,,n) if(sa[i]>k) tp[++ln]=sa[i]-k; rep(i,,mx) ct[i]=;
rep(i,,n) ct[rk[i]]++;
rep(i,,mx) ct[i]+=ct[i-];
per(i,n,) sa[ct[rk[tp[i]]]--]=tp[i]; swap(tp,rk);
mx=;
rep(i,,n){
int I=sa[i],J=sa[i-];
if(tp[I]!=tp[J]||tp[min(I+k,n+)]!=tp[min(J+k,n+)]) ++mx;
rk[I]=mx;
}
}
} void get_ht(){
rep(i,,n) lg[i]=lg[i>>]+; rep(i,,n){//ht[rk[i]]
if(rk[i]==) continue;
int v=ht[rk[i-]];if(v>) v--;
int J=sa[rk[i]-];
while(s[i+v]==s[J+v]) v++;
ht[rk[i]]=v;
} rep(i,,n) st[][i]=ht[i];
rep(j,,V) rep(i,,n)
st[j][i]=min(st[j-][i],st[j-][min(i+(<<(j-)),n)]);
} void init(){
get_sa();
get_ht();
} int Q(int l,int r){
if(l>r) swap(l,r);
if(l==r) return L+;
if(r>n) return ;
l++;
int j=lg[r-l+];
return min(st[j][l],st[j][r-(<<j)+]);
}
}
using Sa::Q;
using Sa::rk; namespace Tr{
const int M=(<<)-,H=(<<)-;
int mi[M+],zuo[M+],you[M+]; void build(){
rep(i,H+,M){
zuo[i]=you[i]=i-H;
mi[i]=L+;
}
per(i,H,){
zuo[i]=zuo[i<<];
you[i]=you[i<<|];
mi[i]=L+;
}
} void cg(int x,int val){
x+=H;mi[x]=val;
while(x!=){
x>>=;
mi[x]=min(mi[x],val);
}
} int rt,ctr; bool ck(int miv,int pos){
return (rt+)<Q(pos,ctr)+miv;
//即 (rt-miv+1)<Q(pos,ctr)
//即 递减函数小于递增函数
} int calc(int x){
if(x==ctr) return ;
return min(max(rt-mi[x+H]+,),Q(ctr,x));
} int get_mi(int x,int oi){//oi=0 oi=1
while((x<<)<M){
if(mi[x<<|oi]<=rt) x=(x<<)|oi;
else x=(x<<)|(oi^);
}
return x;
} int Qr(int R,int C){//修改rt ctr
rt=R;ctr=C; int ans=,x,miv,pos; x=ctr+H;miv=L+;
while(x!=){
x>>=;
if(x&) continue;
if(ck(min(miv,mi[x|]),you[x|])) miv=min(mi[x|],miv);
else{
x|=;
break;
}
}
while((x<<)<M){
int ls=(x<<);
if(ck(min(miv,mi[ls]),you[ls])){
miv=min(miv,mi[ls]);
x=(x<<|);
}
else x=ls;
}
pos=x-H; ans=max(calc(pos),ans);
//往左的第一个 mi[pos]<=rt
while(x!=){
if(x&){
if(mi[x^]<=rt){
x=x^;
break;
}
}
x>>=;
}
pos=get_mi(x,)-H;
ans=max(calc(pos),ans); x=ctr+H;miv=L+;
while(x!=){
x>>=;
if(x&){
if(ck(min(miv,mi[x^]),zuo[x^])) miv=min(miv,mi[x^]);
else{
x^=;
break;
}
}
}
while((x<<)<M){
int rs=(x<<|);
if(ck(min(miv,mi[rs]),zuo[rs])){
miv=min(miv,mi[rs]);
x=(x<<);
}
else x=rs;
}
pos=x-H; ans=max(calc(pos),ans);
//往右第一个
while(x!=){
if((x&)==){
if(mi[x|]<=rt){
x|=;
break;
}
}
x>>=;
}
pos=get_mi(x,)-H; ans=max(calc(pos),ans); return ans;
}
}
using Tr::cg;
using Tr::Qr; struct Query{
int l,r,no;
}a[N];
bool cmp(Query aa,Query bb){
return aa.l>bb.l;
}
vector<int> ps[N];
int hd[N],no[N],bg[N]; typedef long long ll;
ll ans[N]; int main(){
scanf("%s",s+);
L=n=strlen(s+); m=rd();
rep(i,,m){
s[++n]='#';
scanf("%s",s++n);
bg[i]=n;
n+=strlen(s++n); rep(j,bg[i]+,n) hd[j]=i,no[j]=n-j+;
a[i]=(Query){rd(),rd(),i};
} Sa::init(); rep(i,,n){
rep(j,sa[i],n) cerr<<s[j];
cerr<<endl;
} rep(i,,n) ps[hd[sa[i]]].pb(i); sort(a+,a++m,cmp);
Tr::build();
int R=L;
rep(i,,m){
while(a[i].l<=R) cg(rk[R],R) ,R--;
rep(j,,ps[a[i].no].size()-){
int lcp=,pos=ps[a[i].no][j];
if(j) lcp=Q(pos,ps[a[i].no][j-]);
lcp=max(lcp,Qr(a[i].r,pos)); if(no[sa[pos]]>lcp) ans[a[i].no]+=no[sa[pos]]-lcp;
}
} rep(i,,m) printf("%lld\n",ans[i]);
return ;
}
//得到sa ht st存ht
//vector存每个询问的位置 //l从大到小对询问排序
//处理T的重复子串
//线段树加点 线段树上二分最小值

loj#2720. 「NOI2018」你的名字的更多相关文章

  1. LOJ 2720 「NOI2018」你的名字——后缀自动机

    题目:https://loj.ac/problem/2720 自己总是分不清 “SAM上一个点的 len[ ] ” 和 “一个串的前缀在 SAM 上匹配的 len ”. 于是原本想的 68 分做法是, ...

  2. 【LOJ】#2720. 「NOI2018」你的名字

    题解 把S串建一个后缀自动机 用一个可持久化权值线段树维护每个节点的right集合是哪些节点 求本质不同的子串我们就是要求T串中以每个点为结束点的串有多少在\(S[l..r]\)中出现过 首先我们需要 ...

  3. LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增

    题面: https://loj.ac/problem/2720 考虑枚举T串的每个后缀i,我们要做两件事. 一.统计有多少子串[i,j]在S中要求位置出现. 二.去重. 第二步好做,相当于在后缀数组上 ...

  4. 「NOI2018」你的名字

    「NOI2018」你的名字 题目描述 小A 被选为了\(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经 把除了题目命名以外的工作都做好了. 由于\(ION\) 已经举办了很多届 ...

  5. LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)

    题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...

  6. loj#2718. 「NOI2018」归程

    题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...

  7. loj#2721. 「NOI2018」屠龙勇士

    题目链接 loj#2721. 「NOI2018」屠龙勇士 题解 首先可以列出线性方程组 方程组转化为在模p意义下的同余方程 因为不保证pp 互素,考虑扩展中国剩余定理合并 方程组是带系数的,我们要做的 ...

  8. Loj #2719. 「NOI2018」冒泡排序

    Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...

  9. loj 2719 「NOI2018」冒泡排序 - 组合数学

    题目传送门 传送门 题目大意 (相信大家都知道) 显然要考虑一个排列$p$合法的充要条件. 考虑这样一个构造$p$的过程.设排列$p^{-1}_{i}$满足$p_{p^{-1}_i} = i$. 初始 ...

随机推荐

  1. python学习------面向对象的程序设计

    一 面向对象的程序设计的由来 1940年以前:面向机器 最早的程序设计都是采用机器语言来编写的,直接使用二进制码来表示机器能够识别和执行的指令和数 据.简单来说,就是直接编写 和 的序列来代表程序语言 ...

  2. python学习------文件处理

    文件操作 一 介绍 计算机系统分为:计算机硬件,操作系统,应用程序三部分. 我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件,众所周 ...

  3. Mysql 了解changeBuffer 与 purge 调优

    需要删除.新增记录或更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB 会将这些更新操作缓存在 change buffer中, ...

  4. Shell脚本 自动部署 SpringBoot 应用

    公司项目使用了SpringBoot.开发的应用需要自动上传到服务器.虽然目前对热部署还没完全掌握.先使用shell简化一下部署吧. # 上传密钥 sshLoginKey=/f/MyFile/root. ...

  5. Linux内核分析--进程创建,执行,切换

    学号:351 原创作品转载请注明出处本实验来源 https://github.com/mengning/linuxkernel/ 实验要求 从整理上理解进程创建.可执行文件的加载和进程执行进程切换,重 ...

  6. input输入框失去焦点,软键盘关闭后,滚动的页面无法恢复到原来位置

    H5微信页面开发,软键盘弹起后,若原输入框被遮挡,页面整体将会上移,然而当输入框失焦,软键盘收起后,页面未恢复,导致弹框里的按钮响应区域错位. 解决方案:给输入框(或select选择框)添加失去焦点的 ...

  7. django中的Q查询

    转载于:https://mozillazg.com/2015/11/django-the-power-of-q-objects-and-how-to-use-q-object.html 原文写的很详细 ...

  8. jvm参数及分析工具

    -Xmx4G 设置堆的最大内存大小为4GB,也可通过-XX:MaxHeapSize=4GB进行设置 -Xms256m 设置堆的初始内存大小为256兆,如果未设置此选项,则初始大小将设置为新生代和年老代 ...

  9. 后台管理系统之“图片上传” --vue

    图片上传(基于vue) 相信上传图片是所有系统必备的功能吧,工作中的第一个管理系统就在上传图片的功能上卡顿了一整天. 当时用的elementUI组件,但是由于样式和设计图样式差别较大再加上原生相较好理 ...

  10. Oracle数据库 Synonym和DBLink

    1.1数据库创建同义词(synonym) Oracle的同义词(synonyms)从字面上理解就是别名的意思,和视图的功能类似,就是一种映射关系.它可以节省大量的数据库空间,对不同用户的操作同一张表没 ...