题解

写后缀树真是一写就好久,然后调好久QAQ

我们把两个串取反拼一起建后缀树,这样的话使得后缀树是正串的后缀树

然后我们把询问挂在每个节点上,每次线段树合并,对于大于50的每次暴力跳着在线段树找,对于小于50的建出一棵树来,也就是\(a[i][j]\)表示第\(i\)位往后\(j\)位,向下一个\(a[t][j]\)有\(t >= j\)连一条边

这是一棵树,在树上倍增就行了

说起算法来很容易吧!!!!写起来特别烦QAQ

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define pdi pair<db,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define eps 1e-8
#define MAXN 100005
#define mo 974711
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int N,LA,LB,c[MAXN * 2],Ncnt,F = 50;
int64 K;
char A[MAXN],B[MAXN];
int fa[MAXN * 4][19],up[MAXN * 4][19];
struct node {
int cnt,len;
node *nxt[26],*par;
}pool[MAXN * 4],*tail = pool,*root,*last,*que[MAXN * 4];
vector<pii > son[MAXN * 4];
vector<int> sm[MAXN * 4];
int ed[MAXN * 4],posA[MAXN],posB[MAXN],dis[MAXN * 4];
int64 ans[MAXN],sum[MAXN * 4][19];
struct qry_node {
int s,t,len,id;
friend bool operator < (const qry_node &a,const qry_node &b) {
return a.len < b.len;
}
};
vector<qry_node> Qry[MAXN * 4];
namespace segtr {
struct node {
int minv,lc,rc;
}tr[MAXN * 20];
int Ncnt = 0,rt[MAXN * 4];
#define LC(u) tr[u].lc
#define RC(u) tr[u].rc
#define MIN(u) tr[u].minv
void update(int u) {
MIN(u) = LA + 1;
if(LC(u)) {
MIN(u) = min(MIN(u),MIN(LC(u)));
}
if(RC(u)) {
MIN(u) = min(MIN(u),MIN(RC(u)));
}
}
void Insert(int &u,int L,int R,int v) {
if(!u) u = ++Ncnt;
if(L == R) {MIN(u) = v;return;}
int mid = (L + R) >> 1;
if(v <= mid) Insert(LC(u),L,mid,v);
else Insert(RC(u),mid + 1,R,v);
update(u);
}
int Merge(int u,int v) {
if(!u) return v;
if(!v) return u;
LC(u) = Merge(LC(u),LC(v));
RC(u) = Merge(RC(u),RC(v));
update(u);
return u;
}
int Query(int u,int L,int R,int p) {
if(!u) return LA + 1;
if(L >= p) return MIN(u);
int mid = (L + R) >> 1;
if(p <= mid) return min(Query(LC(u),L,mid,p),MIN(RC(u)) != 0 ? MIN(RC(u)) : LA + 1);
else return Query(RC(u),mid + 1,R,p);
}
}
using segtr::rt;
using segtr::Insert;
using segtr::Merge;
using segtr::Query;
void build_SAM(int c,int len) {
node *nowp = tail++,*p;
nowp->len = len;nowp->cnt = 1;
for(p = last ; p && !p->nxt[c] ; p = p->par) {
p->nxt[c] = nowp;
}
if(!p) nowp->par = root;
else {
node *q = p->nxt[c];
if(q->len == p->len + 1) nowp->par = q;
else {
node *copyq = tail++;
*copyq = *q;copyq->cnt = 0;copyq->len = p->len + 1;
q->par = nowp->par = copyq;
for(; p && p->nxt[c] == q ; p = p->par) {
p->nxt[c] = copyq;
}
}
}
last = nowp;
}
void build_SuffixTree() {
int m = tail - pool;
Ncnt = m;
for(int i = 0 ; i < m ; ++i) {
c[pool[i].len]++;
}
for(int i = 1 ; i <= LA + LB ; ++i) c[i] += c[i - 1];
for(int i = 0 ; i < m ; ++i) {
que[c[pool[i].len]--] = &pool[i];
}
for(int i = 1 ; i <= m ; ++i) {
int f = que[i]->par - pool + 1;
}
for(int i = m ; i > 1 ; --i) {
int u = que[i] - pool + 1;
int f = que[i]->par - pool + 1;
fa[u][0] = f;
son[f].pb(mp(u,que[i]->len - que[i]->par->len));
if(que[i]->cnt) {
if(que[i]->len <= LA) {
ed[u] = LA - que[i]->len + 1;
posA[LA - que[i]->len + 1] = u;
}
else posB[LB - (que[i]->len - LA) + 1] = u;
}
}
}
void dfs(int u) {
int s = son[u].size();
for(int i = 0 ; i < s ; ++i) {
dis[son[u][i].fi] = dis[u] + son[u][i].se;
dfs(son[u][i].fi);
}
}
void Process(int u) {
int s = son[u].size(),t;
for(int i = 0 ; i < s ; ++i) {
Process(son[u][i].fi);
rt[u] = Merge(rt[u],rt[son[u][i].fi]);
}
if(ed[u]) Insert(rt[u],1,LA,ed[u]);
sort(Qry[u].begin(),Qry[u].end());
if(sm[u].size()) sort(sm[u].begin(),sm[u].end());
s = Qry[u].size(),t = sm[u].size();
for(int i = 0 ; i < s ; ++i) {
if((i == 0 || Qry[u][i].len != Qry[u][i - 1].len) && Qry[u][i].len <= F) {
int r = 0;
for(int j = 0 ; j < t ; ++j) {
while(r < t && sm[u][r] - sm[u][j] < Qry[u][i].len) ++r;
sum[sm[u][j]][0] = 0;
if(r >= t) up[sm[u][j]][0] = 0;
else {up[sm[u][j]][0] = sm[u][r];sum[sm[u][j]][0] = sm[u][r];}
}
for(int k = 1 ; k <= 18 ; ++k) {
for(int j = 0 ; j < t ; ++j) {
up[sm[u][j]][k] = up[up[sm[u][j]][k - 1]][k - 1];
sum[sm[u][j]][k] = sum[sm[u][j]][k - 1] + sum[up[sm[u][j]][k - 1]][k - 1];
}
}
}
if(Qry[u][i].len <= F) {
int h = Query(rt[u],1,LA,Qry[u][i].s);
if(h + Qry[u][i].len - 1 <= Qry[u][i].t) {
ans[Qry[u][i].id] += K - h;
for(int k = 18 ; k >= 0 ; --k) {
if(up[h][k]) {
if(up[h][k] + Qry[u][i].len - 1 <= Qry[u][i].t) {
ans[Qry[u][i].id] += K * (1 << k) - sum[h][k];
h = up[h][k];
}
}
}
}
}
else {
int h,le = Qry[u][i].s;
while(1) {
h = Query(rt[u],1,LA,le);
if(h + Qry[u][i].len - 1 <= Qry[u][i].t) {
ans[Qry[u][i].id] += K - h;
le = h + Qry[u][i].len;
}
else break;
}
}
}
}
void Solve() {
read(N);read(K);
root = last = tail++;
scanf("%s",A + 1);scanf("%s",B + 1);
LA = strlen(A + 1);LB = strlen(B + 1);
int tot = 0;
for(int i = LA ; i >= 1 ; --i) {
build_SAM(A[i] - 'a',++tot);
}
for(int i = LB ; i >= 1 ; --i) {
build_SAM(B[i] - 'a',++tot);
}
build_SuffixTree();
for(int j = 1 ; j <= 18 ; ++j) {
for(int i = 1 ; i <= Ncnt ; ++i) {
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
}
int s,t,l,r,q;
dfs(1);
read(q);
for(int i = 1 ; i <= q ; ++i) {
read(s);read(t);read(l);read(r);
int h = posB[l];
for(int j = 18 ; j >= 0 ; --j) {
if(dis[fa[h][j]] >= (r - l + 1)) h = fa[h][j];
}
Qry[h].pb((qry_node){s,t,r - l + 1,i});
}
for(int i = 1 ; i <= LA ; ++i) {
int t = posA[i];
for(int j = 18 ; j >= 0 ; --j) {
if(dis[fa[t][j]] >= F) t = fa[t][j];
}
while(t) {
sm[t].pb(i);
t = fa[t][0];
}
}
Process(1);
for(int i = 1 ; i <= q ; ++i) {
out(ans[i]);enter;
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
}

【LOJ】#2525. 「HAOI2018」字串覆盖的更多相关文章

  1. 「HAOI2018」字串覆盖

    「HAOI2018」字串覆盖 题意: ​ 给你两个字符串,长度都为\(N\),以及一个参数\(K\),有\(M\)个询问,每次给你一个\(B\)串的一个子串,问用这个字串去覆盖\(A\)串一段区间的最 ...

  2. loj#2128. 「HAOI2015」数字串拆分 矩阵乘法

    目录 题目链接 题解 代码 题目链接 loj#2128. 「HAOI2015」数字串拆分 题解 \(f(s)\)对于\(f(i) = \sum_{j = i - m}^{i - 1}f(j)\) 这个 ...

  3. 「NOIP2002」「Codevs1099」 字串变换(BFS

    1099 字串变换 2002年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold   题目描述 Description 已知有两个字串 $A$, ...

  4. 【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)

    [BZOJ5304][HAOI2018]字串覆盖(后缀数组,主席树,倍增) 题面 BZOJ 洛谷 题解 贪心的想法是从左往右,能选就选.这个显然是正确的. 题目的数据范围很好的说明了要对于询问分开进行 ...

  5. loj#2574. 「TJOI2018」智力竞赛 (路径覆盖)

    目录 题目链接 题解 代码 题目链接 loj#2574. 「TJOI2018」智力竞赛 题解 就是求可重路径覆盖之后最大化剩余点的最小权值 二分答案后就是一个可重复路径覆盖 处理出可达点做二分图匹配就 ...

  6. Loj #3055. 「HNOI2019」JOJO

    Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...

  7. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

  8. Loj #3057. 「HNOI2019」校园旅行

    Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...

  9. LOJ #6436. 「PKUSC2018」神仙的游戏(字符串+NTT)

    题面 LOJ #6436. 「PKUSC2018」神仙的游戏 题解 参考 yyb 的口中的长郡最强选手 租酥雨大佬的博客 ... 一开始以为 通配符匹配 就是类似于 BZOJ 4259: 残缺的字符串 ...

随机推荐

  1. 解决telnet不是内部命令

    1.telnet在win7下默认是不开启的,所以需要我们自己手动开启.那么首先我们点击开始菜单,找到控制面板项,点击进入: 2.进入程序和功能模块,我们在左边需要选择“打开或关闭windows功能”, ...

  2. 今天GG

    刚开考: 这里锅了,那里锅了,还被D了QAQ. 然后\(YL\)说,\(T2\)不是傻逼题吗. 于是萝卜秒掉了\(T1\). 于是\(gsy\)秒掉了\(T3\). \(lalaxu,FlashHu\ ...

  3. BZOJ4912 [Sdoi2017]天才黑客 【虚树 + 最短路】

    题目链接 BZOJ4912 题解 转移的代价是存在于边和边之间的 所以把边看做点,跑最短路 但是这样做需要把同一个点的所有入边和所有出边之间连边 \(O(m^2)\)的连边无法接受 需要优化建图 膜一 ...

  4. vue单页面应用项目优化总结(转载)

    转载自:https://blog.csdn.net/qq_42221334/article/details/81907901这是之前在公司oa项目优化时罗列的优化点,基本都已经完成,当时花了点心思整理 ...

  5. 基于asp.net + easyui框架,一步步学习easyui-datagrid——实现分页和搜索(二)

    http://blog.csdn.net/jiuqiyuliang/article/details/19967031 目录: 基于asp.net + easyui框架,一步步学习easyui-data ...

  6. Excel:公式中的这些特殊数字

    19E+307 9E+307是科学计数法表示的一个数字,就简单理解成是Excel支持的一个很大的数字就可以了. 用法示例: =LOOKUP(9E+307,A:A) 根据LOOKUP函数的性质,提取A列 ...

  7. SQL语句(十)查询结果排序

    查询结果排序 使用ORDER BY 子句 SELECT <列名列表> FROM <表名> [WHERE 条件] ORDER BY <字段名1> [ASC|DESC] ...

  8. IO 复习字节流字符流拷贝文件

    /* 本地文件 URL 文件拷贝 *//*文本文件拷贝 可以通过 字符流,也可以通过字节流*/ /*二进制文件拷贝 只可以通过字节流*//* 希望这个例子能帮助搞懂 字符流与字节流的区别 */ imp ...

  9. raphael.js 给元素 hover 添加glow() 外发光

    用raphael.js 给 svg画布里面添加个元素,嗯就圓好了,男人一般都喜欢圆形的东西,比如xx ,  xxx , 还有xxx $(document).ready(function() { var ...

  10. iOS 中nil,Nil,NULL,NSNull的区别

    类与对象的概念 类是对同一类事物高度的抽象,类中定义了这一类对象所应具有的静态属性(属性)和动态属性(方法). 对象是类的一个实例,是一个具体的事物. 类与对象是抽象与具体的关系. 类其实就是一种数据 ...