[噼昂!]game(Pending)
壹、题目描述 ¶
不想编了。
贰、关于题解 ¶
注意是子串,不是子序列......该问题实际上和期望并没有什么关系,它的本质是一道计数题,因此不要被表面的伪装吓走了。
自认为题解说得很清楚,就在题解上再修改一些地方放到这里了。
由于每个长度 \(K\) 对应的总方案数是确定的,所以我们就是要计算三种情况的
数量。
我们考虑计算 \(X\le Y\) 的方案数,考虑对串 \(A\#B\) 构建后缀数组,对于每一个属
于 \(A\) 的后缀,排在它后面的属于 \(B\) 的后缀和它产生的贡献是 \(1-\min(SA,SB)\)长度的答案均 \(+1\),其中 \(SA,SB\) 分别表示两个后缀的长度。考虑暴力,从后往前,遇到 \(B\) 后缀时,将 \(1-SB\) 的计数器均 \(+1\),遇到 \(A\) 后缀时,将 \(1-SA\) 的计数器都加进 \(1-SA\) 的答案中去。
对于 \(A\) 后缀,在它前面的 \(B\) 后缀的贡献是 \(1-{\text{LCP}}(SA,SB)\) 的答案 \(+1\). 考虑类似的暴力,从前往后,在遇到后缀 \(B\) 时,将 \(1-K\)(或者 \(1-SB\),是一样的)的计数器 \(+1\),遇到每一个 \(height\) 都将大于 \(height\) 的计数器清零,遇到一个后缀 \(A\),就将 \(1-height_A\) 的计数器加进答案。
于是我们需要维护一个结构,能够将 \(1-x\) 的计数器 \(+1\),\(1-x\) 的计数器加进答案,\(x-K\) 的贡献清零。考虑这样一个数组,每一项是 \((1, counter, ans)\),\(counter\) 表示当前位置计数器,\(ans\) 表示当前位置累计答案,则对于增加贡献的操作,相当于对区间乘上一个 \(\begin{pmatrix}1&1&0\\0&1&0\\0&0&1\end{pmatrix}\),将计数器加进答案的操作相当于对区间乘上一个矩阵 \(\begin{pmatrix}1&0&0\\0&1&1\\0&0&1\end{pmatrix}\),清空计数器相当于对区间乘上一个矩阵 \(\begin{pmatrix}1&0&0\\0&0&0\\0&0&1\end{pmatrix}\),于是我们只需要一个结构能够维护不停的给区间乘上一个矩阵即可,这可以用线段树来实现。
不过维护 \(3\times 3\) 的矩阵太慢了,注意到
于是我们可以只用维护 \(2\times 2\) 的矩阵。
\(X\ge Y\) 的情况同理。我们用这两种方案数减去总的方案数就得到了相等的方案数。然后我们就能计算得到小于和大于的方案数。
时间复杂度 \(\mathcal O(C(N+M)\log (N+M))\),其中 \(C\) 为计算矩阵的常数。
叁、参考代码 ¶
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
# define USING_STDIN
// # define NDEBUG
// # define NCHECK
#include <cassert>
namespace Elaina {
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
template <class T> inline T fab(T x) { return x < 0 ? -x : x; }
template <class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
template <class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }
#ifndef USING_STDIN
inline char freaGET() {
# define BUFFERSIZE 1 << 17
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARGET freaGET()
#else
# define CHARGET getchar()
#endif
template <class T> inline T readret(T x) {
x=0; int f = 0; char c;
while((c = CHARGET) < '0' || '9' < c) if(c == '-') f = 1;
for(x = (c^48); '0' <= (c = CHARGET) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template <class T> inline void readin(T& x) { x = readret(T(1)); }
template <class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template <class T> inline void writc(T x, char s = '\n') {
static int fwri_sta[55], fwri_ed = 0;
if(x < 0) putchar('-'), x = -x;
do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x);
while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed);
putchar(s);
}
} using namespace Elaina;
const int maxn = 200000 << 1 ^ 1;
char s[maxn + 5];
int lena, lenb, n, ans_lim;
inline void input() {
scanf("%s", s + 1), lena = n = strlen(s + 1);
s[++n] = '$'; // split note
scanf("%s", s + n + 1), lenb = strlen(s + n + 1);
n += lenb;
ans_lim = min(lena, lenb);
}
int sa[maxn + 5], height[maxn + 5];
namespace Suffix_array {
const int sigma = 122;
int x[maxn << 1 ^ 1], y[maxn << 1 ^ 1], buc[maxn + 5];
inline void buildSA() {
int m = sigma;
rep(i, 1, n) ++buc[x[i] = s[i]];
rep(i, 1, m) buc[i] += buc[i - 1];
drep(i, n, 1) sa[buc[x[i]]--] = i;
for(int k = 1; k <= n; k <<= 1) {
int siz = 0;
rep(i, n - k + 1, n) y[++siz] = i;
rep(i, 1, n) if(sa[i] - k >= 1) y[++siz] = sa[i] - k;
rep(i, 0, m) buc[i] = 0;
rep(i, 1, n) ++buc[x[i]];
rep(i, 1, m) buc[i] += buc[i - 1];
drep(i, n, 1) sa[buc[x[y[i]]]--] = y[i], y[i] = 0;
swap(x, y); x[sa[1]] = 1; int lev = 1;
rep(i, 2, n) x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? lev : ++lev);
if(lev == n) break;
m = lev;
}
}
int rnk[maxn + 5];
inline void buildHeight() {
int h = 0;
rep(i, 1, n) rnk[sa[i]] = i;
rep(i, 1, n) {
if(rnk[i] == 1) continue;
if(h) --h;
int j = sa[rnk[i] - 1];
// open interval, s[i : i + h - 1] = s[j : j + h - 1]
while(i + h <= n && j + h <= n && s[i + h] == s[j + h]) ++h;
height[rnk[i]] = h;
}
}
} // using namespace Suffix_array;
struct matrix {
ll a[2][2];
inline matrix(int A = 0, int B = 0, int C = 0, int D = 0) {
a[0][0] = A, a[0][1] = B, a[1][0] = C, a[1][1] = D;
}
inline void one() { (*this) = matrix(0, 0, 1, 0); }
inline bool ifone() {
if(a[0][0] == 0 && a[0][1] == 0 && a[1][0] == 1 && a[1][1] == 0)
return true;
return false;
}
// updated multiplication
inline matrix operator * (const matrix& rhs) {
matrix ret;
ret.a[0][0] = a[0][0] * rhs.a[1][0] + rhs.a[0][0];
ret.a[0][1] = a[0][1] + a[0][0] * rhs.a[1][1] + rhs.a[0][1];
ret.a[1][0] = a[1][0] * rhs.a[1][0];
ret.a[1][1] = a[1][0] * rhs.a[1][1] + a[1][1];
return ret;
}
inline void print() const {
rep(i, 0, 1) {
printf("%d ", i ^ 1);
rep(j, 0, 1) printf("%d ", a[i][j]);
Endl;
}
printf("0 0 1\n");
}
};
const matrix Clear = matrix();
const matrix Trans = matrix(0, 0, 1, 1);
const matrix Plus = matrix(1, 0, 1, 0);
namespace saya {
matrix tag[maxn << 2 | 2];
ll delta[maxn << 2 | 2], ans[maxn << 2 | 2];
#define _root int i = 1, int l = 1, int r = ans_lim
#define ls (i << 1)
#define rs (i << 1 | 1)
#define mid ((l + r) >> 1)
#define _lhs ls, l, mid
#define _rhs rs, mid + 1, r
void build(_root) {
tag[i].one();
delta[i] = ans[i] = 0;
if(l == r) return;
build(_lhs), build(_rhs);
}
inline void impact(int i, const matrix& opt) {
tag[i] = tag[i] * opt;
// lawks! :> order
ans[i] = ans[i] + delta[i] * opt.a[1][1] + opt.a[0][1];
delta[i] = delta[i] * opt.a[1][0] + opt.a[0][0];
}
inline void pushdown(int i) {
if(tag[i].ifone()) return;
impact(ls, tag[i]), impact(rs, tag[i]);
tag[i].one();
}
void modify(int ql, int qr, const matrix& opt, _root) {
if(ql > qr) return;
if(ql <= l && r <= qr) return impact(i, opt);
pushdown(i);
if(ql <= mid) modify(ql, qr, opt, _lhs);
if(mid < qr) modify(ql, qr, opt, _rhs);
}
void release(ll* arr, _root) {
if(l == r) {
arr[l] += ans[i];
return;
}
pushdown(i);
release(arr, _lhs), release(arr, _rhs);
}
#undef _root
#undef ls
#undef rs
#undef mid
#undef _lhs
#undef _rhs
} // using namespace saya;
ll lower[maxn + 5], upper[maxn + 5];
inline void solveAlowerB() {
saya::build();
drep(i, n, 1) if(sa[i] ^ (lena + 1)) {
// a suffix for string B
if(sa[i] > lena) saya::modify(1, n - sa[i] + 1, Plus);
else saya::modify(1, lena - sa[i] + 1, Trans);
}
saya::release(lower);
saya::build();
rep(i, 1, n) if(sa[i] ^ (lena + 1)) {
if(sa[i] > lena + 1) saya::modify(1, n - sa[i] + 1, Plus);
else saya::modify(1, height[i], Trans);
saya::modify(height[i + 1] + 1, ans_lim, Clear);
}
saya::release(lower);
}
inline void solveAupperB() {
saya::build();
rep(i, 1, n) if(sa[i] ^ (lena + 1)) {
if(sa[i] > lena) saya::modify(1, n - sa[i] + 1, Plus);
else saya::modify(1, lena - sa[i] + 1, Trans);
}
saya::release(upper);
saya::build();
drep(i, n, 1) if(sa[i] ^ (lena + 1)) {
if(sa[i] > lena) saya::modify(1, n - sa[i] + 1, Plus);
else saya::modify(1, height[i + 1], Trans);
saya::modify(height[i] + 1, ans_lim, Clear);
}
saya::release(upper);
}
ll gcd(ll x, ll y) {
return y ? gcd(y, x % y) : x;
}
inline void printFrac(ll a, ll b) {
if(a == 0) printf("0/1");
else {
ll g = gcd(a, b);
printf("%lld/%lld", a / g, b / g);
}
}
inline void printAns() {
rep(i, 1, ans_lim) {
ll all = 1ll * (lena - i + 1) * (lenb - i + 1);
ll equal = lower[i] + upper[i] - all;
ll strictLow = all - upper[i];
ll strictUpp = all - lower[i];
printFrac(strictLow, all), putchar(' ');
printFrac(equal, all), putchar(' ');
printFrac(strictUpp, all), Endl;
}
}
signed main() {
// freopen("game.in", "r", stdin);
// freopen("game.out", "w", stdout);
input();
Suffix_array::buildSA();
Suffix_array::buildHeight();
solveAlowerB();
solveAupperB();
printAns();
return 0;
}
肆、关键 の 地方 ¶
其实仔细想一想,许多概率期望题的本质都是计数问题,所以,组合学得好,期望没烦恼啊。