#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 < ...
随机推荐
- 图--生成树和最小生成树.RP
树(自由树).无序树和有根树 自由树就是一个无回路的连通图(没有确定根)(在自由树中选定一顶点做根,则成为一棵通常的树). 从根开始,为每个顶点(在树中通常称作结点)的孩子规定从左到右的次 ...
- boost::python开发环境搭建
本来想用mingw编译boost::python模块,网上看了下资料太少,只有使用vs2012 操作环境:win7 x64 python: x86 boost: 1.57 编译boost::pytho ...
- 数据结构_calculator
问题描述 小 V 发明了一个神奇的整数计算器:给定一个合法的表达式,这个计算器能求出这个表达式的最终答案.表达式可能包含:+:运算符,整数加法.如 1+1=2-:运算符,整数减法.如 1-1=0*:运 ...
- HTML完全使用详解 PDF扫描版
<HTML完全使用详解>根据网页制作的实际特点和目前市场需要,全面系统地介绍了最新的HTML4.01.丰富的实例贯穿全书,能帮助您全面掌握HTML,而且本书所有实例均可直接修改使用,可以提 ...
- SqlBulkCopy(批量复制)使用方法 && SqlDataAdapter Update
SqlBulkCopy提供了一种将数据复制到Sql Server数据库表中高性能的方法.SqlBulkCopy 包含一个方法 WriteToServer,它用来从数据的源复制数据到数据的目的地. Wr ...
- 【转载】在AspNetCore 中 使用Redis实现分布式缓存
原文地址:https://www.cnblogs.com/szlblog/p/9045209.html AspNetCore 使用Redis实现分布式缓存 上一篇讲到了,Core的内置缓存:IMemo ...
- uint8_t / uint16_t / uint32_t /uint64_t 是什么数据类型
在nesc的代码中,你会看到很多你不认识的数据类型,比如uint8_t等.咋一看,好像是个新的数据类型,不过C语言(nesc是C的扩展)里面好像没有这种数据类型啊!怎么又是u又是_t的?很多人有这样的 ...
- Knights0.
Knights t数轴上有n个骑士位于1,2,3,...n,移动速度相同,初始移动方向已知,当两个骑士相遇时,各有50%的概率赢,输了就死了,并且移动到0和n+1的位置时移动方向会翻转,问最右的骑士存 ...
- 洛谷P3648 [APIO2014]序列分割(斜率优化)
传送门 没想到这种多个状态转移的还能用上斜率优化……学到了…… 首先我们可以发现,切的顺序对最终答案是没有影响的 比方说有一个序列$abc$,每一个字母都代表几个数字,那么先切$ab$再切$bc$,得 ...
- 2019.2.14 考试T1 FFT
\(\color{#0066ff}{ 题目描述 }\) 衡水二中的机房里经常有人莫名其妙地犇雷,leizi很生气,决定要找出那个犇雷的人 机房有n个人,每个人都认为机房里有两个人可能会犇雷,其中第i个 ...