0%

八省联考2018 制胡窜

除了代码有点难调还是挺妙的

题意:将一个字符串SS分为三段[1,i],[i+1,j1],[j,n][1,i],[i+1,j-1],[j,n] 至少有一段包含了子串Sl,rS_{l,r}的方案数

至少有一段不太好统计,考虑总共的方案数是(n12)\binom{n-1}{2},如果能减去三段全不包含的就能得到答案

至少我们得知道Sl,rS_{l,r}的所有位置,那假设我们就知道了(?)所有形如[li,ri][l_i,r_i]的区间是Sl,rS_{l,r}的位置,为了方便设有mm个区间且按ll从小到大排序

首先可以发现如果有三个子串不重叠必然为00

否则分类讨论

Case 1:

如果最左的子串和最右的子串重叠,即r1>lmr_1>l_{m}

那么我们对ii的选取讨论一下首先肯定是在[1,r1)[1,r_1)

如果i[1,l1)i\in[1,l_1)那么jj只能选(lm,r1](l_m,r_1],贡献为(l11)(r1lm)(l_1-1)(r_1-l_m)

如果i[li,li+1)i\in[l_i,l_{i+1}),此时jj的限制是(lm,ri+1](l_m,r_{i+1}],贡献为(li+1li)(ri+1lm)\sum(l_{i+1}-l_i)(r_{i+1}-l_m)

如果i[li+1,r1)i\in[l_{i+1},r_1) 仅需保证i+1<ji+1<j,贡献为(r1lm2)+(r1lm)(nr1)\binom{r1-l_m}{2}+(r_1-l_m)(n-r_1)

Case 2:

如果最左的子串和最右的子串不交,即r1<lmr_1<l_m

首先如果i[1,l1)i \in [1,l_1)是没法选的

i[li,li+1]i\in[l_i,l_{i+1}]略麻烦,实际上也能写出这个式子(ri+1ri)(ri+1lm)\sum(r_{i+1}-r_i)(r_{i+1}-l_m) 要求是lm<ri+1l_m<r_{i+1}li+1<r1l_{i+1}<r_1

但实际上会漏掉一些答案,还需要找到r1+len1r_1+len-1endposendpos中的前驱和后继p1,p2p_1,p_2(建议画图理解)贡献为(r1lp1)(rp2lm)(r_1-l_{p1})(r_{p2}-l_m)

所以最终我们要维护区间最大最小,和(ri+1ri)ri+1(r_{i+1}-r_i)r_{i+1}(ri+1ri)(r_{i+1}-r_i)(Case1中求和也可以改成这样)

对于询问先判断是否有三个相交的区间,然后讨论

前后缀可以利用最大最小来查询

至于endposendpos集合(也就是[li,ri][l_i,r_i]),虽然在SAM定义里有这个东西,但实际上建出来的SAM是没有的

但是可以考虑每一个状态的endposendpos是他的linklink的所有点的endposendpos并集,这个东西可以跑线段树合并

具体来说是对于每一个前缀ii插入到权值线段树里,然后dfsdfs搞出来所有的endposendpos

Sl,rS_{l,r}在SAM上的位置可以倍增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 2e5 + 6;
char s[N];
int n, q;
struct Edge {
int v;
int nxt;
} edge[N * 4];
int head[N * 2], ecnt;
void add(int u, int v) {
edge[++ecnt].v = v;
edge[ecnt].nxt = head[u];
head[u] = ecnt;
}
namespace SegmentTree {
const int maxn = 2e5 + 6;
struct Node {
int mmin, mmax;
long long s1, s2;
int ls, rs;
} tr[maxn * 40];
int tot;
Node merge(const Node &a, const Node &b) {
Node c;
c.mmin = min(a.mmin, b.mmin);
c.mmax = max(a.mmax, b.mmax);
c.s1 = a.s1 + b.s1 + b.mmin * 1LL * (b.mmin - a.mmax);
c.s2 = a.s2 + b.s2 + b.mmin - a.mmax;
return c;
}
void pushup(int x) {
if (tr[x].ls && tr[x].rs) {
tr[x].mmin = min(tr[tr[x].ls].mmin, tr[tr[x].rs].mmin);
tr[x].mmax = max(tr[tr[x].ls].mmax, tr[tr[x].rs].mmax);
tr[x].s1 =
tr[tr[x].ls].s1 + tr[tr[x].rs].s1 +
tr[tr[x].rs].mmin * 1LL * (tr[tr[x].rs].mmin - tr[tr[x].ls].mmax);
tr[x].s2 = tr[tr[x].ls].s2 + tr[tr[x].rs].s2 + tr[tr[x].rs].mmin -
tr[tr[x].ls].mmax;
} else if (tr[x].ls) {
int t_ls = tr[x].ls;
int t_rs = tr[x].rs;
tr[x] = tr[tr[x].ls];
tr[x].ls = t_ls;
tr[x].rs = t_rs;
} else {
int t_ls = tr[x].ls;
int t_rs = tr[x].rs;
tr[x] = tr[tr[x].rs];
tr[x].ls = t_ls;
tr[x].rs = t_rs;
}
}
int querymax(int rt, int l, int r, int L, int R) {
if (!rt)
return 0;
if (L <= l && r <= R)
return tr[rt].mmax;
int mid = l + r >> 1, ans = 0;
if (L <= mid)
ans = querymax(tr[rt].ls, l, mid, L, R);
if (R > mid)
ans = max(ans, querymax(tr[rt].rs, mid + 1, r, L, R));
return ans;
}
int querymin(int rt, int l, int r, int L, int R) {
if (!rt)
return 0x3f3f3f3f;
if (L <= l && r <= R)
return tr[rt].mmin;
int mid = l + r >> 1, ans = 0x3f3f3f3f;
if (L <= mid)
ans = querymin(tr[rt].ls, l, mid, L, R);
if (R > mid)
ans = min(ans, querymin(tr[rt].rs, mid + 1, r, L, R));
return ans;
}
Node c_res;
void cquery(int rt, int l, int r, int L, int R) {
if (!rt)
return;
if (L <= l && r <= R) {
if (c_res.mmin == 0)
c_res = tr[rt];
else
c_res = merge(c_res, tr[rt]);
return;
}
int mid = l + r >> 1;
if (L <= mid)
cquery(tr[rt].ls, l, mid, L, R);
if (R > mid)
cquery(tr[rt].rs, mid + 1, r, L, R);
}
void update(int &rt, int l, int r, int p) {
if (!rt)
rt = ++tot;
if (l == r) {
tr[rt].mmin = tr[rt].mmax = p;
tr[rt].s1 = tr[rt].s2 = 0;
tr[rt].ls = tr[rt].rs = 0;
return;
}
int mid = l + r >> 1;
if (p <= mid)
update(tr[rt].ls, l, mid, p);
else
update(tr[rt].rs, mid + 1, r, p);
pushup(rt);
}
int merge(int x, int y) {
if (!x || !y)
return x + y;
int rt = ++tot;
tr[rt].ls = merge(tr[x].ls, tr[y].ls);
tr[rt].rs = merge(tr[x].rs, tr[y].rs);
pushup(rt);
return rt;
}
} // namespace SegmentTree

int rt[N], dep[N], fa[N * 2][19];
int pos[N];
namespace SAM {
int nxt[N * 2][10], link[N * 2], len[N * 2];
int cnt, last;
void insert(int c, int id) {
int cur = ++cnt;
int p = last;
last = cur;
pos[id] = cur;
while (p && !nxt[p][c]) {
nxt[p][c] = cur;
p = link[p];
}
if (!p) {
link[cur] = 1;
} else {
int q = nxt[p][c];
if (len[p] + 1 == len[q]) {
link[cur] = q;
} else {
int cq = ++cnt;
len[cq] = len[p] + 1;
memcpy(nxt[cq], nxt[q], sizeof(nxt[q]));
link[cq] = link[q];
link[q] = link[cur] = cq;
while (p && nxt[p][c] == q) {
nxt[p][c] = cq;
p = link[p];
}
}
}
}
void dfs(int x);
void build() {
for (int i = 1; i <= cnt; i++) {
if (link[i]) {
add(link[i], i);
}
}
for (int i = 1; i <= n; i++) {
SegmentTree::update(rt[pos[i]], 1, n, i);
}
dfs(1);
}
void dfs(int x) {
fa[x][0] = link[x];
for (int i = 1; i <= 18; i++) {
fa[x][i] = fa[fa[x][i - 1]][i - 1];
}
for (int i = head[x]; i; i = edge[i].nxt) {
int v = edge[i].v;
dep[v] = dep[x] + 1;
dfs(v);
if (x != 1) {
rt[x] = SegmentTree::merge(rt[x], rt[v]);
}
}
}
} // namespace SAM
ll C2(int n) {
if (n < 2) {
return 0;
}
return 1ll * n * (n - 1) / 2;
}
long long querym(int l, int r) {
using SAM::len;
using namespace SegmentTree;
int plen = r - l + 1, x = pos[r];
for (int i = 18; i >= 0; i--) {
if (len[fa[x][i]] >= plen) {
x = fa[x][i];
}
}
int L = tr[rt[x]].mmin, R = tr[rt[x]].mmax;
if (L < R - plen * 2 + 1 &&
querymax(rt[x], 1, n, L, R - plen) - plen + 1 > L)
return C2(n - 1);
if (R - plen + 1 <= L) {
Node now = tr[rt[x]];
int lm = R - plen + 1;
long long ans =
now.s1 - now.s2 * lm + C2(L - lm) + (L - lm) * 1LL * (n - plen);
return C2(n - 1) - ans;
} else {
c_res = (Node){0, 0, 0, 0, 0, 0};
int lm = R - plen + 1, poslm = querymax(rt[x], 1, n, 1, lm);
cquery(rt[x], 1, n, poslm, L + plen - 1);
Node now = c_res;
int p1 = querymax(rt[x], 1, n, 1, L + plen - 1);
int p2 = querymin(rt[x], 1, n, L + plen, n);
long long ans = now.s1 - now.s2 * lm +
(p2 > lm ? (L - (p1 - plen + 1)) * 1LL * (p2 - lm) : 0);
return C2(n - 1) - ans;
}
}
int main() {
cin >> n >> q;
cin >> s + 1;
SAM::last = SAM::cnt = 1;
for (int i = 1; i <= n; i++) {
SAM::insert(s[i] - '0', i);
}
SAM::build();
while (q--) {
int l, r;
cin >> l >> r;
cout << querym(l, r) << endl;
}
return 0;
}

Asusetic eru quionours