#1413 : Rikka with String 后缀自动机 + 二级差分
http://hihocoder.com/problemset/problem/1413?sid=1199641
这题断断续续做了2个多星期吧,一直不会
设总答案为sum,替换后新加的子串数量为x,失去的是y,那么每个位置的答案就是sum + x[i] - y[i]
首先可以知道如果把某个位置设置成'#',那么肯定有i * (len - i + 1)个新的不同的子串
比如是aa#cb,左边有i个选择,右边有len - i + 1个选择,根据组合数学就是i * (len - i + 1)个不同的子串
然后替换过后,就会有一些原本有的子串被删除了。
对于每一个状态,可以拓扑出它的mxpos和mipos也就是endpos的两个位置。
那么对于一个长度是len的子串,是否删除 了这个字符后 在整个字符串中不再出现,可以这样判断
如果mxpos - len + 1(也就是这个长度是len的子串的最大开始位置),如果这个位置还 < mipos那么如果删除了这个位置
肯定会丢失这个长度是len的字符串了。可以画个图吧看看
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;
const int maxn = 3e5 + , N = ;
struct SAM {
int mxCnt[maxn << ], son[maxn << ][N], fa[maxn << ], pos[maxn << ];
int flag[maxn << ][]; //是否前缀节点
int mi[maxn << ], mx[maxn << ];
int root, last, DFN, t;
int create() {
++t;
mxCnt[t] = pos[t] = fa[t] = NULL;
// mi[t] = inf, mx[t] = -inf;
for (int i = ; i < ; ++i) flag[t][i] = NULL;
for (int i = ; i < N; ++i) son[t][i] = NULL;
return t;
}
void init() {
++DFN;
t = , root = ;
last = create();
}
void addChar(int x, int _pos, int id) { // _pos表示在原串中的位置
int p = last;
int np = create();
last = np;
mxCnt[np] = mxCnt[p] + , pos[np] = _pos, flag[np][id] = DFN; //前缀节点
for (; p && son[p][x] == NULL; p = fa[p]) son[p][x] = np;
if (p == NULL) {
fa[np] = root;
return;
}
int q = son[p][x];
if (mxCnt[q] == mxCnt[p] + ) {
fa[np] = q;
return;
}
int nq = create(); //用来代替q的,默认不是前缀节点
flag[nq][id] = DFN - ; //默认不是前缀节点
pos[nq] = pos[q]; //pos要和q相同
for (int i = ; i < N; ++i) son[nq][i] = son[q][i];
fa[nq] = fa[q], mxCnt[nq] = mxCnt[p] + ;
fa[q] = nq, fa[np] = nq;
for (; p && son[p][x] == q; p = fa[p]) son[p][x] = nq;
}
int dp[maxn << ], in[maxn << ], que[maxn << ];
void topo() { //多次使用不用清空
for (int i = ; i <= t; ++i) {
in[fa[i]]++;
mi[i] = mx[i] = pos[i];
}
int head = , tail = ;
for (int i = ; i <= t; ++i) {
if (in[i] == ) que[tail++] = i;
}
while (head < tail) {
int cur = que[head++];
if (cur == root) break;
mx[fa[cur]] = max(mx[fa[cur]], mx[cur]);
in[fa[cur]]--;
if (in[fa[cur]] == ) que[tail++] = fa[cur];
}
}
} sam;
LL cnt[maxn], sub[maxn];
void add(int be, int en, LL val, LL d) {
cnt[be] += val;
cnt[en + ] -= d * (en - be) + val;
sub[be + ] += d;
sub[en + ] -= d;
}
void init(int en) {
for (int i = ; i <= en; ++i) {
sub[i] += sub[i - ];
cnt[i] += cnt[i - ] + sub[i];
}
} char str[maxn]; void work() {
int len;
cin >> len;
sam.init();
scanf("%s", str + );
LL ans = ;
for (int i = ; str[i]; ++i) {
sam.addChar(str[i] - 'a', i, );
}
sam.topo();
// int fuck = 5;
// printf("%d\n", sam.mx[fuck + 1]);
for (int i = ; i <= sam.t; ++i) {
ans += sam.mxCnt[i] - sam.mxCnt[sam.fa[i]];
if (sam.mx[i] - sam.mxCnt[i] + <= sam.mi[i]) {
int be = sam.mx[i] - sam.mxCnt[i] + ;
int en = min(sam.mx[i] - sam.mxCnt[sam.fa[i]], sam.mi[i]);
int len = en - be + ;
// printf("%d %d\n", be, en);
add(be, en, , );
be = en + ;
en = sam.mi[i]; //还有一段,要全部加上len个
// printf("%d %d\n\n", be, en); if (be <= en) add(be, en, len, ); }
}
init(len);
// printf("%d\n", cnt[4]);
for (int i = ; i <= len; ++i) {
printf("%lld ", ans + 1LL * (i) * (len - i + ) - cnt[i]);
} } int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
work();
return ;
}
#1413 : Rikka with String 后缀自动机 + 二级差分的更多相关文章
- 【hihocoder#1413】Rikka with String 后缀自动机 + 差分
搞了一上午+接近一下午这个题,然后被屠了个稀烂,默默仰慕一晚上学会SAM的以及半天4道SAM的hxy大爷. 题目链接:http://hihocoder.com/problemset/problem/1 ...
- HDU 6086 Rikka with String AC自动机 + DP
Rikka with String Problem Description As we know, Rikka is poor at math. Yuta is worrying about this ...
- 牛客多校第四场 I string 后缀自动机/回文自动机
这个回文自动机的板有问题,它虽然能过这道题,但是在计算size的时候会出锅! 题意: 求一个字符串中本质不同的连续子串有几个,但是某串和它反转后的字符串算一个. 题解: 要注意的是,一般字符串题中的“ ...
- hdu 6086 -- Rikka with String(AC自动机 + 状压DP)
题目链接 Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, s ...
- Codeforces 917F Substrings in a String - 后缀自动机 - 分块 - bitset - KMP
题目传送门 传送点I 传送点II 传送点III 题目大意 给定一个字母串,要求支持以下操作: 修改一个位置的字母 查询一段区间中,字符串$s$作为子串出现的次数 Solution 1 Bitset 每 ...
- 识别子串 (string)——后缀自动机+线段树
题目 [题目描述] 一般地,对于一个字符串 S,和 S 中第 $ i $ 个字符 x,定义子串 $ T=S(i.j) $ 为一个关于 x 的识别子申,当且仅当: 1.$ i \leq x \leq j ...
- cf1121F. Compress String(后缀自动机)
题意 题目链接 Sol 居然出个SAM板子也是没谁了233 #include<bits/stdc++.h> #define Pair pair<int, int> #defin ...
- Tjoi2019 甲苯先生和大中锋的字符串 后缀自动机_差分
tjoi胆子好大,直接出了两道送分题...... 都 9102 年了,还有省选出模板题QAQ...... Code: #include <bits/stdc++.h> #define se ...
- bzoj 5408: string 后缀自动机 + LCT
联赛前练练码力. code: #include <vector> #include <cstdio> #include <cstring> #include < ...
随机推荐
- Oracle数据库之多表查询一
上一篇给大家介绍了数据的单表查询,相信大家对于单表的查询应该都有了一些了解.单表查询在数据库中的使用会有一些,但并不是很多,但是作为初学者,我们需要学习单表查询的思路.今天呢,我们为大家介绍一下数据库 ...
- python:格式化输出整数
import math #default print "PI = %f" % math.pi #width = 10,precise = 3,align = left print ...
- Java 在本地开发环境部署多个 spring 项目
修改Tomcat 的 server.xml 文件 路径:C:\JAVA\apache_tomcat_8.5.11\conf\server.xml : 每个web项目的端口号不同,且存储的目录相同但是文 ...
- .NET 图片上传接收类
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- C# GDI
绘制实心矩形 using (Graphics gp = Graphics.FromImage(bmBlank)) { //... ; Rectangle rec = , y, , );//画一个白块, ...
- TensorFlow创建变量
1 使用tf.Variable函数创建变量 tf.Variable(initial_value=None,trainable=True,collections=None,validate_shape= ...
- spark(3) - scala独立编程
>>非集成: 环境需要 * spark 2.4.0 * scala 2.11.12 * sbt (打包) 开发过程 1.shell命令下创建项目目录结构 *****/ project / ...
- JAVA进阶----ThreadPoolExecutor机制(转)
http://825635381.iteye.com/blog/2184680 ThreadPoolExecutor机制 一.概述 1.ThreadPoolExecutor作为java.util.co ...
- 使用Spring IOC容器引用外部属性文件
一.引用外部属性文件 1.编写属性文件,以键值对形式存储,并放置在类路径(src)下 jdbc.jdbcUrl=jdbc:mysql://localhost:3306/BOOKSTORE?rewrit ...
- Tarjan+树形DP【洛谷P2515】[HAOI2010]软件安装
[洛谷P2515][HAOI2010]软件安装 题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得 ...