这题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. Day 4-9 subprocess模块

    我们经常需要通过Python去执行一条系统命令或脚本,系统的shell命令是独立于你的python进程之外的,每执行一条命令,就是发起一个新进程,通过python调用系统命令或脚本的模块在python ...

  2. C# Note26: [MethodImpl(MethodImplOptions.Synchronized)]与lock机制

    在进行.NET开发时,经常会遇见如何保持线程同步的情况.在众多的线程同步的可选方式中,加锁无疑是最为常用的.如果仅仅是基于方法级别的线程同步,使用System.Runtime.CompilerServ ...

  3. freemarker 简单操作

    操作字符串函数 1. substring(start,end)从一个字符串中截取子串 start:截取子串开始的索引,start必须大于等于0,小于等于end end: 截取子串的长度,end必须大于 ...

  4. B站弹幕姬(🐔)分析与开发(下篇)

    接上篇 下面开始分析弹幕信息: 当我们在完成和服务器的握手之后,b站的弹幕服务器就会开始不停的开始给我们推送相关房间的弹幕消息了.老规矩前面16个字节是这个包的相关信息,我们先来看下这条消息的前面16 ...

  5. ASP.NET Web.config文件的配置(Configuration API)

    本次我们讨论主要聚焦在以下Web.config配置文件的设置值的读取. 1.<connectionString />连接字符串的读取. 2.<appSettings />应用程 ...

  6. CMD & Git Shell & Bash Shell

    CMD & Git Shell & Bash Shell https://mvdan.cc/sh/cmd/shfmt PC

  7. ajax获得后台传来的一个json值,在js中获得其中的属性值

    首先 ajax的dataType需要设置为json, 默认的text获取属性值在jquery3.2.1中尝试不成功 获得属性值的方式: 类似数组,键值对的方式 下面例子: 设置dataType为jso ...

  8. centos无网络问题

  9. Memcached 分布式集群

    首先解释一下我的标题,用到了 分布式 和 集群两个单词,为什么是集群?解决[相同业务]问题的服务器多个以上就称为集群.这里memcached就是做相同任务的(提供缓存服务)为什么是分布式?虽然针对的是 ...

  10. Jira的搭建

    一.环境准备 jira7.2的运行是依赖java环境的,也就是说需要安装jdk并且要是1.8以上版本,如下: java -version 除此之外,我们还需要为jira创建对应的数据库.用户名和密码, ...