链接大合集:

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. c# 类的历遍和历遍操作

    string id = Request.Form["id"]; string type = Request.Form["type"]; string info ...

  2. Windows 10 远程桌面出现身份验证错误:要求的函数不受支持(解决)

    爬梯子还是快一些,百度搜一年也搜不到~~~囧 给出原始地址,百度的某些解决方案不知道是不是截取过来搞错了还是啥,返回和原始方案就差一丢丢,浪费时间. https://windowsreport.com ...

  3. OO第二单元的总结

    三周复三周,一轮又一轮,我似乎已经将OO是为我的生活必须品了.在与同学吐槽者身负-3楼与20楼重任的A电梯君,以及我们都是上一层下两层不用电梯的五号青年的等等欢声笑语中结束了第二轮的OO作业.当然这次 ...

  4. Leet Code 3. Longest Substring Without Repeating Characters

    Given a string, find the length of the longest substring without repeating characters. Examples: Giv ...

  5. c++学习过程

    作者本人也是一名初学者,我的QQ:2522929921,可以一起交流啊! 希望广大初学者能够一起进步: 1.掌握编程思维真的很重要!!!!***. 2.不能刻意记忆语法规则. 3.在循序渐进的项目实战 ...

  6. Groovy学习笔记-使用多赋值

    1.方法返回多个结果:返回数组,将多个变量逗号隔开,放在左侧圆括号中 def splitName (fullName) { fullName.split(' ') } def (firstName, ...

  7. Qt终结者之QML动画

    前言 使用QML差不多2年了,在使用过程中深深的感受到QML的强大与便捷,让我深陷其中,不能自拔.其中QML相比传统的界面最大的优势就是便捷的动画效果与炫酷的粒子效果,让QML做出来的界面能媲美WPF ...

  8. 北大poj- 1009

    Edge Detection Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 22835   Accepted: 5398 D ...

  9. datetime模块

    # 其中days = -2,可以根据需要进行替换,这样就可以得到不同需要的日期了. # # 另外:可以通过strftime方法,指定时间的输出格式. # # 除了以上输入的   %Y-%m-%d    ...

  10. 【原创】Arduino入门基础知识总结

     一.概述   Arduino是源自意大利的一个开放源代码的硬件项目平台,该平台包括一块具备简单I/O功能的电路板以及一套程序开发环境软件. Arduino可以用来开发交互产品,比如它可以读取大量的开 ...