【题目链接】

  • 点击打开链接

【思路要点】

  • 考虑将字符串缩成若干个相同字符的段 (cnt,char)(cnt,char)(cnt,char) ,相邻的字符不相同。
  • 考虑一个与某一个前缀匹配的后缀,若该后缀横跨了 x(x≥2)x\ (x\geq2)x (x≥2) 段,那么其对应的第 222 段至第 x−1x-1x−1 段的 (cnt,char)(cnt,char)(cnt,char) 都应该对应相等;且后缀的第 111 段与前缀的第 111 段的 charcharchar 相等,后缀的 cntcntcnt 不小于前缀的 cntcntcnt ;后缀的第 xxx 段与前缀的第 xxx 段的 charcharchar 相等,前缀的 cntcntcnt 不小于后缀的 cntcntcnt 。
  • 离线将字符树建出来,然后求出各个点的 fail/nextfail/nextfail/next ,注意这里的匹配允许当前匹配的第 111 段的 cntcntcnt 不小于前缀第 111 段的 cntcntcnt ,具体实现时可以使用可持久化线段树实现回退。
  • 接下来求解答案,我们需要计算新产生的前缀 newnewnew 对答案的贡献。当前节点 curcurcur 的 failfailfail 树上的每一个节点 fafafa 都存在后继字符 charsonfachar_{son_{fa}}charsonfa​​ 及其长度 cntsonfacnt_{son_{fa}}cntsonfa​​ ,若 charsonfa=charnewchar_{son_{fa}}=char_{new}charsonfa​​=charnew​ ,则对于每一个 1≤i≤Min{cntsonfa,cntnew}1\leq i\leq Min\{cnt_{son_{fa}},cnt_{new}\}1≤i≤Min{cntsonfa​​,cntnew​} ,新产生的第 iii 个前缀将存在一个长度为 depthfa+idepth_{fa}+idepthfa​+i 的匹配。且由于 depthfadepth_{fa}depthfa​ 是递增的,可以将其看做一次区间赋值操作,用可持久化线段树维护各个字符的各个长度的匹配以实现回退。
  • 注意特殊处理匹配没有横跨 222 段的情况。
  • 时间复杂度 O(NLogN)O(NLogN)O(NLogN) 。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXP = 5e6 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {x = 0; int f = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';x *= f;
}
template <typename T> void write(T x) {if (x < 0) x = -x, putchar('-');if (x > 9) write(x / 10);putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {write(x);puts("");
}
struct SegmentTreeWithInfo {struct Node {int lc, rc;int val;} a[MAXP];int root, size, n;void init(int x) {n = x;root = size = 0;}int modify(int root, int l, int r, int pos, int d) {int ans = ++size;a[ans] = a[root];if (l == r) {a[ans].val = d;return ans;}int mid = (l + r) / 2;if (mid >= pos) a[ans].lc = modify(a[root].lc, l, mid, pos, d);else a[ans].rc = modify(a[root].rc, mid + 1, r, pos, d);return ans;}int modify(int root, int pos, int d) {return modify(root, 1, n, pos, d);}int query(int root, int l, int r, int pos) {if (l == r) return a[root].val;int mid = (l + r) / 2;if (mid >= pos) return query(a[root].lc, l, mid, pos);else return query(a[root].rc, mid + 1, r, pos);}int query(int root, int pos) {return query(root, 1, n, pos);}
} Edge;
struct SegmentTreeWithAnswer {struct Node {int lc, rc;int tag; ll sum;} a[MAXP];int root, size, n;void init(int x) {n = x;root = size = 0;}void update(int root) {a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum;}void pushdown(int root, int l, int r) {if (a[root].tag) {int mid = (l + r) / 2;int lc = ++size;a[lc] = a[a[root].lc];a[lc].tag = a[root].tag;a[lc].sum = 1ll * (mid - l + 1) * a[root].tag;a[root].lc = lc;int rc = ++size;a[rc] = a[a[root].rc];a[rc].tag = a[root].tag;a[rc].sum = 1ll * (r - mid) * a[root].tag;a[root].rc = rc;a[root].tag = 0;}}int modify(int root, int l, int r, int ql, int qr, int d) {int ans = ++size;a[ans] = a[root];if (l == ql && r == qr) {a[ans].tag = d;a[ans].sum = (r - l + 1ll) * d;return ans;}pushdown(ans, l, r);int mid = (l + r) / 2;if (mid >= ql) a[ans].lc = modify(a[ans].lc, l, mid, ql, min(mid, qr), d);if (mid + 1 <= qr) a[ans].rc = modify(a[ans].rc, mid + 1, r, max(mid + 1, ql), qr, d);update(ans);return ans;}int modify(int root, int pos, int d) {return modify(root, 1, n, 1, pos, d + 1);}int getMax(int root, int l, int r) {if (a[root].tag) return r;int mid = (l + r) / 2;if (a[a[root].rc].sum) return getMax(a[root].rc, mid + 1, r);else return getMax(a[root].lc, l, mid);}ll getsum(int root, int l, int r, int ql, int qr) {if (l == ql && r == qr) return a[root].sum;if (a[root].tag) return (qr - ql + 1ll) * a[root].tag;int mid = (l + r) / 2; ll ans = 0;if (mid >= ql) ans += getsum(a[root].lc, l, mid, ql, min(qr, mid));if (mid + 1 <= qr) ans += getsum(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);return ans;}ll query(int root, int pos, int least) {if (a[root].sum == 0) return 0;int Max = getMax(root, 1, n); ll ans = 0;if (Max < pos) {if (least <= Max) ans = 1ll * (pos - Max) * least;else if (least >= pos) ans = (Max + 1ll + pos) * (pos - Max) / 2;else ans = 1ll * least * (least - Max) + (least + 1ll + pos) * (pos - least) / 2;}chkmin(pos, Max);return ans + getsum(root, 1, n, 1, pos) + pos * (pos - 1ll) / 2;}
} ST;
struct AcAutomaton {struct Node {vector <int> sons, ori;int depth, redge, rans, fail;char c; ll ans;} a[MAXN];char key; int first, firstp;map <pair <int, char>, int> home;int cnte, Max, size, tot, pos[MAXN];void update(pair <int, char> x) {if (!home.count(x)) home[x] = ++cnte;}void rebuild(int pos, char c, int fa) {for (auto x : a[pos].ori) {if (a[x].c == c) {a[fa].sons.push_back(x);update(make_pair(a[x].depth - a[fa].depth, a[x].c));rebuild(x, a[x].c, fa);} else {a[pos].sons.push_back(x);update(make_pair(a[x].depth - a[pos].depth, a[x].c));rebuild(x, a[x].c, pos);}}}void getfail(int pos) {//cerr << pos << ' ' << a[pos].fail << endl;for (auto x : a[pos].sons) {chkmax(Max, a[x].depth - a[pos].depth);a[x].fail = Edge.query(a[a[pos].fail].redge, home[make_pair(a[x].depth - a[pos].depth, a[x].c)]);if (a[x].fail == 0 && a[x].c == key && a[x].depth - a[pos].depth >= first) a[x].fail = firstp;a[x].redge = a[a[x].fail].redge;int bak = a[pos].redge;a[pos].redge = Edge.modify(a[pos].redge, home[make_pair(a[x].depth - a[pos].depth, a[x].c)], x);if (pos == 0) key = a[x].c, first = a[x].depth - a[pos].depth, firstp = x;getfail(x);a[pos].redge = bak;}}ll func(int x) {return x * (x - 1ll) / 2;}void getans(int pos) {for (auto x : a[pos].sons) {int troot = Edge.query(a[a[pos].fail].redge, a[x].c - 'a' + 1);if (pos == 0) {a[x].ans = a[pos].ans + func(a[x].depth - a[pos].depth);key = a[x].c, first = a[x].depth - a[pos].depth;} else a[x].ans = a[pos].ans + ST.query(troot, a[x].depth - a[pos].depth, first * (a[x].c == key));a[x].redge = a[a[x].fail].redge;int bak = a[pos].redge; troot = Edge.query(a[pos].redge, a[x].c - 'a' + 1);troot = ST.modify(troot, a[x].depth - a[pos].depth, a[pos].depth);a[pos].redge = Edge.modify(a[pos].redge, a[x].c - 'a' + 1, troot);getans(x);a[pos].redge = bak;}}void work() {rebuild(0, -1, 0);Edge.init(cnte);getfail(0);Edge.init(26);ST.init(Max);getans(0);for (int i = 1; i <= tot; i++)writeln(a[pos[i]].ans % 998244353);}void extend(int x, char c) {pos[++tot] = ++size;a[size].c = c;a[size].depth = a[pos[tot - 1]].depth + x;a[pos[tot - 1]].ori.push_back(size);}void rev(int x) {pos[++tot] = pos[x];}
} ACAM;
int main() {int n; read(n);for (int i = 1; i <= n; i++) {int opt, x; char c;read(opt), read(x);if (opt == 1) {c = getchar();ACAM.extend(x, c);} else ACAM.rev(x);}ACAM.work();return 0;
}

【LOJ3055】「HNOI2019」JOJO相关推荐

  1. Loj #3055. 「HNOI2019」JOJO

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

  2. 【C++】「JSOI-2008」魔兽地图DotR

    「JSOI-2008」魔兽地图DotR [来源] [题目描述] [输入格式] [输出格式] [样例输出] [样例输出] [解析] [代码] [来源] BZOJ-1017 计蒜客-T2793 vjudg ...

  3. 【LOJ3124】「CTS2019」氪金手游

    [题目链接] 点击打开链接 [思路要点] 考虑给出的图为外向树的情况,各个点都需要早于子树中所有的点,记 sizeisize_isizei​ 表示 iii 子树中所有点的 wiw_iwi​ 之和,则获 ...

  4. 【LOJ2867】「IOI2018」高速公路收费

    [题目链接] 点击打开链接 [思路要点] 首先,我们显然需要令所有边为 AAA 进行一次询问,得到 sss 到 ttt 的最短路. 我们可以从找到最短路上的一个点出发: 令所有与编号在 [1,mid] ...

  5. 【题解】「JSOI2012」玄武密码(AC自动机)

    题面 [题目描述] 在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河.相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中.老人们说,这是玄武神灵将天书藏匿在此. 很多年 ...

  6. 【转】「一个」、「梨视频」、「必应词典」、「金山词典」、「豆瓣电影」、「12306」等应用的 API

    「一个」.「梨视频」.「必应词典」.「金山词典」.「豆瓣电影」.「12306」等应用的 API https://segmentfault.com/p/1210000017941187?utm_sour ...

  7. 【LOJ3103】「JSOI2019」节日庆典

    [题目链接] 点击打开链接 [思路要点] 考虑一种暴力维护候选点集的做法. 即,在字符串不断增长的同时,若已经可以确定 TiT_iTi​ 不再可能成为字典序最小的循环后缀,则将 iii 在候选点集中删 ...

  8. 【LOJ2983】「WC2019」数树

    [题目链接] 点击打开链接 [思路要点] op=0op=0op=0 ,算一算两棵树的公共边数即可. 时间复杂度 O(N)O(N)O(N) 或 O(NLogN)O(NLogN)O(NLogN) . op ...

  9. 【LOJ3047】「ZJOI2019」浙江省选

    [题目链接] 点击打开链接 [思路要点] 对于 M = 1 M=1 M=1 的情况,问题即为求半平面交. 考虑 M M M 更大的情况,以 M = 2 M=2 M=2 为例. 首先去掉排名可以为第一的 ...

最新文章

  1. php和css一样吗,php和css一样吗
  2. 腾讯最大股东收购了 Stack Overflow,以后“抄代码”都要付费了么?
  3. Java基础笔记17
  4. 老毛桃u盘启动pe重建mbr图文教程
  5. Worker启动Executor源码
  6. qtp查询mysql_QTP中测试数据库连接
  7. Linux中如何用命令打开文件夹
  8. Linux常用的基本命令head、tail、tar、grep、date、cal(二)
  9. C语言“fread”函数的用法?
  10. 人工智能大脑如何调控智能交通“疏堵”?
  11. Mycat适合场景及不适合场景
  12. 使用python实现栈和队列
  13. TestNG 框架的运用
  14. mysql添加一个字段(
  15. Xcode不出错误提示,Indexing | Processing files
  16. java开发业务流程图,什么是业务流程图?业务流程图如何绘制?
  17. Java的jdk安装与环境变量配置
  18. 【DIY】热水器升级加装远程wifi控制功能,esp8266远程红外控制热水器启动
  19. 无线串口服务器规模,无线串口服务器
  20. Win11下蓝牙鼠标停止4秒后启动卡顿

热门文章

  1. 新视野大学英语第三版读写教程2答案
  2. Map中可以作为Key的类型
  3. IDEA + github pull request + teamcity运用
  4. HTML 如何获取输入框的值
  5. AVR单片机及其编译软件
  6. Lammps实现水分子在纳米颗粒球表面的吸附行为
  7. PCIe接口二,三事
  8. 戒烟产品如何引流?戒烟产品如何寻找客户?戒烟类产品推广方案
  9. Python学习随笔:使用xlwings读取和操作Excel文件
  10. “耐克 Nike”被美国GBC代理,附问题答疑