这题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. mysql数据库在linux上的不同登录方式和权限

    在我的上两篇博文里,一篇是安装,一篇是配置远程登录, 提君博客原创 >>提君博客原创  http://www.cnblogs.com/tijun/  << 所以我的mysql的 ...

  2. 保存后自动格式化代码(vscode)

    痛点: 写项目的时候, 我们经常会拷贝一些代码, 每当拷贝过来都需要重新调整, 如果可以实现保存自动调整代码, 将会给我们带来很多的便利! 解决: 其实对于vscode来说, 实现这一点很容易. 我们 ...

  3. vue bug & data type bug

    vue bug & data type bug [Vue warn]: Invalid prop: type check failed for prop "value". ...

  4. IWMS后台上传文章,嵌入音频文件代码

    <object width="260" height="69" classid="clsid:6bf52a52-394a-11d3-b153-0 ...

  5. CSAW 2016:Rock-100

    ELF文件 拖进ida 震惊: 这一堆堆都是什么神仙函数呀!! f5看一下伪代码 可以看出来 其实看起来函数很多 但是真正管用的函数也就那么几个 如果sub_4017E6的值是0 那么就会输出flag ...

  6. 5.cgroup资源控制

    控制组( CGroups)是 Linux 内核的一个特性,主要用来对共享资源进行隔离.限制.审计等. 只有将分配到容器的资源进行控制,才能避免多个容器同时运行时对宿主机系统的资源竞争.每个控制组是一组 ...

  7. 自定义 ASP.NET Identity Data Model with EF

    One of the first issues you will likely encounter when getting started with ASP.NET Identity centers ...

  8. BZOJ1014[JSOI2008]火星人——非旋转treap+二分答案+hash

    题目描述 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 8 9 10 ...

  9. HDU5773-The All-purpose Zero-多校#41010-最长上升子序列问题

    只想到了朴素的n^2做法,然后发现可以用splay维护.于是调了几个小时的splay... splay的元素是从第二个开始的!第一个是之前插入的头节点! #include <cstdio> ...

  10. 洛谷P4513 小白逛公园

    区间最大子段和模板题.. 维护四个数组:prefix, suffix, sum, tree 假设当前访问节点为cur prefix[cur]=max(prefix[lson],sum[lson]+pr ...