「九省联考 2018」制胡窜
好久没更新博客了..
考虑容斥,求切两刀后所有串都被切到至少一次的方案数。
大力分类讨论
设 (s) 在原串中从左到右出现的右端点序列为 (a_1 dots a_m) ,把答案分成以下三个部分
1.第一刀没有切到任意一个字符串,第二刀切完所有字符串
2.第二刀没有切到任意一个字符串,第一刀切完所有字符串
3.第一刀切完所有字符串,第二刀切到任意一个字符串
4.第一刀切到任意一个字符串且没有切完所有字符串,加上第二刀后切完所有字符串
答案就是四类方案数的和
前三类情况的方案数当一刀不能切完所有字符串时方案数之和就是 (0) ,否则方案数之和就是
[x = a_1 - a_m + len(s) - 1 \
x(n-a_m+a_1) - frac{x(x-1)}{2}
]
对于第四类情况,设第二刀能切的第一个串为 (p) ,不难发现 (p) 的范围是一个区间 ([L, R]) ,对于其中每一个串 (i) 作为第二刀的位置,有方案数 ((a_i-a_m+len(s)-1)min(a_i - a_{i-1}, a_1-a_{i-1}+len(s)-1)) 。
考虑用线段树维护 ( ext{Right}) 集合,那么可以在线段树上二分出 (L, R) ,对于后面的 (min) 也有单调性,可以二分出一个位置 (p) 使得 (p) 之后都取后面的项,所以只需要在此基础上维护区间 (sum a_i(a_i-a_{i-1})) ,区间 (0,1,2) 次方和即可,复杂度 (mathcal O(nlog n)) 。
code
/*program by mangoyang*/
#pragma GCC optimize("Ofast", "inline")
#include<bits/stdc++.h>
#define inf ((ll) 2e18)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
const int N = 200005;
vector<int> ddd;
char s[N];
int n, q;
struct Node{
int nl, nr; ll s, sz, sum, s2;
inline Node(){ nl = nr = s = sum = sz = s2 = 0; }
friend Node operator + (const Node &A, const Node &B){
Node C;
C.nl = A.nl ? A.nl : B.nl;
C.nr = B.nr ? B.nr : A.nr;
C.s = A.s + B.s;
if(A.nl && B.nl)
C.s += 1ll * B.nl * (B.nl - A.nr);
C.sz = A.sz + B.sz;
C.sum = A.sum + B.sum;
C.s2 = A.s2 + B.s2;
return C;
}
};
namespace Seg{
#define mid ((l + r) >> 1)
struct Node A[N*30];
int sz[N*30], lc[N*30], rc[N*30], size;
inline void ins(int &u, int l, int r, int pos){
if(!u) u = ++size;
if(l == r){
sz[u] = 1, A[u].nl = A[u].nr = pos;
A[u].sz = 1, A[u].sum = pos, A[u].s2 = 1ll * pos * pos;
return;
}
if(pos <= mid) ins(lc[u], l, mid, pos);
else ins(rc[u], mid + 1, r, pos);
sz[u] = sz[lc[u]] + sz[rc[u]];
A[u] = A[lc[u]] + A[rc[u]];
}
inline int merge(int x, int y){
if(!x || !y) return x + y;
int o = ++size;
lc[o] = merge(lc[x], lc[y]);
rc[o] = merge(rc[x], rc[y]);
sz[o] = sz[lc[o]] + sz[rc[o]];
A[o] = A[lc[o]] + A[rc[o]];
return o;
}
inline int queryL(int u, int l, int r, int k){
if(l == r) return A[u].nr + k > 0 ? l : n + 1;
if(A[lc[u]].nr + k > 0)
return queryL(lc[u], l, mid, k);
else return queryL(rc[u], mid + 1, r, k);
}
inline int queryR(int u, int l, int r, int k){
if(l == r) return l;
if((sz[lc[u]] && k - A[lc[u]].nr <= 0) || !sz[rc[u]])
return queryR(lc[u], l, mid, k);
else return queryR(rc[u], mid + 1, r, k);
}
inline Node query(int u, int l, int r, int L, int R){
if(l >= L && r <= R) return A[u];
if(L > mid) return query(rc[u], mid + 1, r, L, R);
if(R <= mid) return query(lc[u], l, mid, L, R);
return query(lc[u], l, mid, L, R) + query(rc[u], mid + 1, r, L, R);
}
inline int queryGG(int u, int l, int r, int k){
if(l == r) return l > k ? l : n + 1;
if(A[lc[u]].nr > k) return queryGG(lc[u], l, mid, k);
else return queryGG(rc[u], mid + 1, r, k);
}
}
namespace sam{
vector<int> g[N];
int ch[N][10], fa[N], pos[N], rt[N], f[N][21], len[N], size = 1, tail = 1;
inline int newnode(int x){ return len[++size] = x, size; }
inline void ins(int c, int po){
int p = tail, np = newnode(len[p] + 1);
Seg::ins(rt[np], 1, n, po), pos[po] = np;
for(; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if(!p) return (void) (tail = np, fa[np] = 1);
int q = ch[p][c];
if(len[q] == len[p] + 1) fa[np] = q;
else{
int nq = newnode(len[p] + 1);
fa[nq] = fa[q], fa[q] = fa[np] = nq;
for(int i = 0; i < 10; i++) ch[nq][i] = ch[q][i];
for(; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}tail = np;
}
inline void addedge(){
for(int i = 2; i <= size; i++) g[fa[i]].push_back(i);
}
inline void dfs(int u){
for(int i = 1; i <= 20; i++) f[u][i] = f[f[u][i-1]][i-1];
for(auto v : g[u]){
f[v][0] = u, dfs(v);
rt[u] = Seg::merge(rt[u], rt[v]);
}
}
inline int getnode(int l, int r){
int u = pos[r];
for(int i = 20; ~i; i--)
if(len[f[u][i]] >= r - l + 1) u = f[u][i];
return u;
}
inline ll solve(int l, int r){
int x = r - l + 1, u = getnode(l, r);
int nl = Seg::A[rt[u]].nl;
int nr = Seg::A[rt[u]].nr;
ll res = 0;
if(nl - nr + x - 1 > 0){
res += 1ll * (nl - nr + x - 1) * (n - nr + nl - 2);
res -= 1ll * (nl - nr + x - 1) * (nl - nr + x - 2) / 2;
}
int L = Seg::queryL(rt[u], 1, n, x - nr - 1);
int R = Seg::queryR(rt[u], 1, n, x + nl - 1);
if(L > R) return 0;
int pos = max(L + 1, Seg::queryGG(rt[u], 1, n, nl + x - 1));
Node now;
if(R >= pos) now = Seg::query(rt[u], 1, n, pos, R);
res -= now.s2;
res += 1ll * (R - L) * (x - nr - 1);
res += Seg::query(rt[u], 1, n, L, R).s;
res += now.sum * (nl + x - 1);
res -= now.sum * (x - nr - 1);
res += (nl + x - 1) * now.sz * (x - nr - 1);
if(L > R) return 0;
if(L > nl){
int p = Seg::query(rt[u], 1, n, 1, L - 1).nr;
res += 1ll * min(L - p, nl - p + x - 1) * (L + x - nr - 1);
}
return res;
}
}
int main(){
read(n), read(q);
scanf("%s", s + 1);
for(int i = 1; i <= n; i++) sam::ins(s[i] - '0', i);
sam::addedge();
sam::dfs(1);
while(q--){
int L, R; read(L), read(R);
if(n <= 2){ puts("0"); continue; }
ll res = 1ll * n * (n - 1) / 2 - (n - 1);
res -= sam::solve(L, R);
printf("%lld
", res);
}
return 0;
}