链接大合集:

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. ubuntu google chrome 忽略证书错误 -- 解决自签名证书不支持的问题

    ubuntu chrome 打开自签名的证书的服务器的https时,提示 Your connection is not private. 错误代码:NET::ERR_CERT_AUTHORITY_IN ...

  2. windows分驱

    步骤/方法   1 鼠标右击“计算机” 2 选择“管理”标签 3 打开“计算机管理”窗口   选择“磁盘“>>”存储管理“,打开”磁盘管理“页面 如图:   右键单击选择要压缩的磁盘(本例 ...

  3. .NET--------未能加载文件或程序集“System.Net.Http.Formatting”或它的某一个依赖项。

    未能加载文件或程序集“System.Net.Http.Formatting”或它的某一个依赖项.找到的程序集清单定义与程序集引用不匹配. (异常来自 HRESULT:0x80131040)   解决方 ...

  4. webservice接口,用Soapui

    webservice接口怎么测试呢,他不需要你在拼报文了,会给一个webservice的地址,或者wsdl文件,直接在soapui导入,就可以看到这个webservice里面的所有接口,也有报文,直接 ...

  5. pytest自动化6:pytest.mark.parametrize装饰器--测试用例参数化

    前言:pytest.mark.parametrize装饰器可以实现测试用例参数化. parametrizing 1.  下面是一个简单是实例,检查一定的输入和期望输出测试功能的典型例子 2.  标记单 ...

  6. yum安装的Nginx添加第三方模块支持tcp

    需求:生产有个接口是通过socket通信.nginx1.9开始支持tcp层的转发,通过stream实现的,而socket也是基于tcp通信. 实现方法:Centos7.2下yum直接安装的nginx, ...

  7. SLAM for dummies中文翻译

    1.简介 本文的主要目的是简单介绍移动机器人领域中广泛应用的技术SLAM(同步定位与地图绘制)的理论基础以及应用细节.虽然目前存在很多关于SLAM技术的方方面面的论文,但是对于一个新手来说,仍然需要花 ...

  8. 泊爷带你学go -- redis连接池的操作

    package main import ( "common" "fmt" "proto" "strconv" " ...

  9. 北大poj- 1013

    Counterfeit Dollar Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 50515   Accepted: 15 ...

  10. codeblocks报错:cannot open output file bin\Debug\2.exe Permission denied

    在任务管理器中也找不到正在执行的任务,以为清除了,但是重新编译文件报错. 解决办法: 打开W+R窗口,输入taskkill -IM 2.exe /F