这题TM是计数神题......SAM就是个板子,别脑残写错就完事了。有个技巧是快速定位子串,倍增即可。

考虑反着来,就是两个断点切割所有串,求方案数。

大概分类讨论一下......先特判掉一些情况。然后考虑最左右的两个串是否相交。

相交的情况比较友善,先特殊统计有断点在交集中。之后枚举第一个断点切割了1 ~ i个串。第二个断点就切割i+1 ~ m个串。

然后写出一个∑的式子,拆开之后发现维护right集合平方和和right集合相邻元素的乘积即可。

不相交的麻烦点(极其麻烦...)考虑枚举第一个断点切割了1~i个串,这里的i的限制是L ~ R,可以先求出来。

然后继续化简一个∑式子,最后发现要维护的东西差不多。于是这题做完了。

感想:对拍真好用.jpg

 #include <bits/stdc++.h>

 typedef long long LL;
const int N = , M = ; struct Edge {
int nex, v;
}edge[N]; int tp; int tr[N][], fail[N], len[N], rt[N], last = , tot = ;
int ls[M], rs[M], cnt, e[N], ed[N], fa[N][], pw[N];
LL sum0[M], sum2[M], sumd[M], large[M], small[M];
int n, q;
char str[N]; inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} inline void pushup(int o) {
if(!ls[o] && !rs[o]) return;
sum0[o] = sum0[ls[o]] + sum0[rs[o]];
sum2[o] = sum2[ls[o]] + sum2[rs[o]];
sumd[o] = sumd[ls[o]] + sumd[rs[o]];
if(ls[o] && rs[o]) sumd[o] += small[rs[o]] * large[ls[o]];
large[o] = std::max(large[ls[o]], large[rs[o]]);
small[o] = std::min(small[ls[o]], small[rs[o]]);
return;
} int merge(int x, int y) {
if(!x || !y) return x | y;
int o = ++cnt;
large[o] = large[x];
small[o] = small[x];
sum0[o] = sum0[x];
sum2[o] = sum2[x];
sumd[o] = sumd[x];
ls[o] = merge(ls[x], ls[y]);
rs[o] = merge(rs[x] ,rs[y]);
pushup(o);
return o;
} void insert(int p, int l, int r, int &o) {
if(!o) o = ++cnt;
if(l == r) {
large[o] = small[o] = r;
sum0[o] = ;
sum2[o] = 1ll * r * r;
sumd[o] = ;
return;
}
int mid = (l + r) >> ;
if(p <= mid) insert(p, l, mid, ls[o]);
else insert(p, mid + , r, rs[o]);
pushup(o);
return;
} inline void insert(char c, int id) {
int f = c - '', p = last, np = ++tot;
last = np;
insert(id, , n, rt[np]);
len[np] = len[p] + ;
while(p && !tr[p][f]) {
tr[p][f] = np;
p = fail[p];
}
if(!p) {
fail[np] = ;
}
else {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
fail[np] = Q;
}
else {
int nQ = ++tot;
len[nQ] = len[p] + ;
fail[nQ] = fail[Q];
fail[Q] = fail[np] = nQ;
memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
while(tr[p][f] == Q) {
tr[p][f] = nQ;
p = fail[p];
}
}
}
return;
} int ask0(int L, int R, int l, int r, int o) {
if(!o) return ;
if(L <= l && r <= R) return sum0[o];
int mid = (l + r) >> , ans = ;
if(L <= mid) ans += ask0(L, R, l, mid, ls[o]);
if(mid < R) ans += ask0(L, R, mid + , r, rs[o]);
return ans;
} LL ask2(int L, int R, int l, int r, int o) {
if(!o) return ;
if(L <= l && r <= R) return sum2[o];
int mid = (l + r) >> ;
LL ans = ;
if(L <= mid) ans += ask2(L, R, l, mid, ls[o]);
if(mid < R) ans += ask2(L, R, mid + , r, rs[o]);
return ans;
} LL askd(int L, int R, int l, int r, int o) {
if(!o) return ;
if(L <= l && r <= R) return sumd[o];
int mid = (l + r) >> ;
if(R <= mid) return askd(L, R, l, mid, ls[o]);
if(mid < L) return askd(L, R, mid + , r, rs[o]);
LL ans = askd(L, R, l, mid, ls[o]) + askd(L, R, mid + , r, rs[o]);
if(ls[o] && rs[o]) {
ans += large[ls[o]] * small[rs[o]];
}
return ans;
} int getKpos(int k, int l, int r, int o) {
if(l == r) return r;
int mid = (l + r) >> ;
if(k <= sum0[ls[o]]) return getKpos(k, l, mid, ls[o]);
else return getKpos(k - sum0[ls[o]], mid + , r, rs[o]);
} void DFS(int x) {
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
fa[y][] = x;
DFS(y);
rt[x] = merge(rt[x], rt[y]);
}
return;
} inline void prework() {
for(int i = ; i <= tot; i++) pw[i] = pw[i >> ] + ;
for(int j = ; j <= pw[tot]; j++) {
for(int i = ; i <= tot; i++) {
fa[i][j] = fa[fa[i][j - ]][j - ];
}
}
return;
} inline int getPos(int x, int Len) {
int t = pw[tot];
while(t >= && len[fa[x][]] >= Len) {
if(len[fa[x][t]] >= Len) {
x = fa[x][t];
}
t--;
}
return x;
} inline LL getAns(int Len, int root) { if(Len == ) return ; int r1 = small[root], rn = large[root];
int l1 = r1 - Len + , ln = rn - Len + ; if(ask0(r1 + Len - , ln, , n, root)) {
return ;
}
LL ans = ;
if(r1 > ln) { /// cross
ans = sum2[root] - sumd[root] - 1ll * r1 * rn;
LL temp = (r1 - ln);
ans += (temp - ) * temp / + temp * (n - temp - );
}
else {
int ql = ask0(, r1 + Len - , , n, root), qr = ask0(, ln, , n, root);
int L = qr, R = ql;
int rL = getKpos(L, , n, root), rR = getKpos(R, , n, root), nexr = getKpos(R + , , n, root);
ans = 1ll * (r1 - rR + Len - ) * (nexr - ln) + 1ll * rL * ln - 1ll * rR * ln;
ans += ask2(rL + , rR, , n, root) - askd(rL, rR, , n, root);
}
return ans;
} int main() {
memset(small, 0x3f, sizeof(small));
scanf("%d%d", &n, &q);
scanf("%s", str + );
for(int i = ; i <= n; i++) {
insert(str[i], i);
}
int p = ;
for(int i = ; i <= n; i++) {
int f = str[i] - '';
p = tr[p][f];
ed[i] = p;
//printf("i = %d p = %d \n", i, p);
}
for(int i = ; i <= tot; i++) add(fail[i], i);
DFS();
prework();
LL SUM = 1ll * (n - ) * (n - ) / ;
for(int i = , l, r; i <= q; i++) {
scanf("%d%d", &l, &r);
LL t = getAns(r - l + , rt[getPos(ed[r], r - l + )]);
printf("%lld\n", SUM - t);
}
return ;
}

AC代码

洛谷P4384 制胡窜的更多相关文章

  1. Loj #2479. 「九省联考 2018」制胡窜

    Loj #2479. 「九省联考 2018」制胡窜 题目描述 对于一个字符串 \(S\),我们定义 \(|S|\) 表示 \(S\) 的长度. 接着,我们定义 \(S_i\) 表示 \(S\) 中第 ...

  2. 并不对劲的复健训练-bzoj5253:loj2479:p4384:[2018多省联考]制胡窜

    题目大意 给出一个字符串\(S\),长度为\(n\)(\(n\leq 10^5\)),\(S[l:r]\)表示\(S_l,S_{l+1}...,S_r\)这个子串.有\(m\)(\(m\leq 3\t ...

  3. 洛谷P2429 制杖题 [2017年6月计划 数论10]

    P2429 制杖题 题目描述 求不大于 m 的. 质因数集与给定质数集有交集的自然数之和. 输入输出格式 输入格式: 第一行二个整数 n,m. 第二行 n 个整数,表示质数集内的元素 p[i]. 输出 ...

  4. bzoj5253 [2018多省省队联测]制胡窜

    后缀自动机挺好毒瘤的题. 我们考虑哪些切点是不合法的.肯定是所有的匹配串都被切了. 我们考虑第一个切口的位置. 当第一个切口在第一个出现位置前时,第二个切口必须切掉所有的串. 当第一个切口在$l_{i ...

  5. 【LOJ】#2479. 「九省联考 2018」制胡窜

    题解 老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很 现在后缀树上线段树合并差点把我写死 主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了 但就是不想写 但是由于我过于老 ...

  6. 【HEOI 2018】制胡窜

    转载请注明出处:http://www.cnblogs.com/TSHugh/p/8779709.html YJQ的题解把思路介绍得很明白,只不过有些细节说得还是太笼统了(不过正经的题解就应该这个样子吧 ...

  7. [八省联考2018]制胡窜 (SAM+大讨论)

    正着做着实不太好做,正难则反,考虑反着做. 把i,j看成在切割字符串,我们统计有多少对(i,j)会切割所有与\(s_{l,r}\)相同的串.对于在后缀自动机上表示\(s_{l,r}\)的节点x,x的p ...

  8. 洛谷P1017 进制转换

    洛谷P1017 进制转换 题目描述 我们可以用这样的方式来表示一个十进制数: 将每个阿拉伯数字乘以一个以该数字所处位置的(值减1)为指数,以10为底数的幂之和的形式.例如:123可表示为 \(1*10 ...

  9. 洛谷 P1017 进制转换

    推荐洛谷 题目描述 我们可以用这样的方式来表示一个十进制数: 将每个阿拉伯数字乘以一个以该数字所处位置的(值减1)为指数,以10为底数的幂之和的形式.例如:123可表示为 1*10^2+2*10^1+ ...

随机推荐

  1. 查看端口占用cmd命令

    查看端口被占用的进程: 在任务管理器中结束进程:

  2. java 环境变量配置 详解!

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. linux下更改MySQL数据库存储路径

    参考地址:1.https://blog.csdn.net/ArnoBM/article/details/83008212 2.http://www.cnblogs.com/lyongde/p/3725 ...

  4. 洛谷-p2764(最小路径覆盖)(网络流24题)

    #include<iostream> #include<algorithm> #include<queue> #include<cstring> #in ...

  5. gym-101350M

    题意:给你一堆货币汇率,再给你一堆货币,算下值多少钱: 思路:直接map搞定: #include<iostream> #include<algorithm> #include& ...

  6. 【建模应用】PCA主成分分析原理详解

    原文载于此:http://blog.csdn.net/zhongkelee/article/details/44064401 一.PCA简介 1. 相关背景 上完陈恩红老师的<机器学习与知识发现 ...

  7. Java之视频读取IO流解帧实施方案

    获取视频处理对象的方式有很多,读取本地文件.读取url.读取摄像头等,而直接读流解析视频的实施方案却难以寻觅.此处有两种方案处理视频流(此处设定场景为用户上传视频,同时两种方式均需服务端安装ffmpe ...

  8. P1880 [NOI1995]石子合并 区间dp+拆环成链

    思路 :一道经典的区间dp  唯一不同的时候 终点和起点相连  所以要拆环成链  只需要把1-n的数组在n+1-2*n复制一遍就行了 #include<bits/stdc++.h> usi ...

  9. BZOJ4818 [SDOI2017] 序列计数 【矩阵快速幂】

    题目分析: 一个很显然的同类项合并.注意到p的大小最大为100,考虑把模p意义下相同的求出来最后所有的减去没有质数的做矩阵快速幂即可. 代码: #include<bits/stdc++.h> ...

  10. 【XSY2665】没有上司的舞会 LCT DP

    题目大意 有一棵树,最开始只有一个点.每次会往这棵树中加一个点,总共\(n\)次.输出每次加点后树的最大独立集大小. 强制在线. \(n\leq 300000\) 题解 显然是LCT. 那么要维护什么 ...