[噼昂!]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\) 的矩阵太慢了,注意到

\[\begin{pmatrix} 1&a&b \\ 0&c&d \\ 0&0&1 \end{pmatrix} \times \begin{pmatrix} 1&e&f \\ 0&g&h \\ 0&0&1 \end{pmatrix} \\ = \begin{pmatrix} 1&e+ag&f+ah+b \\ 0&cg&ch+d \\ 0&0&1 \end{pmatrix} \]

于是我们可以只用维护 \(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;
}

肆、关键 の 地方 ¶

其实仔细想一想,许多概率期望题的本质都是计数问题,所以,组合学得好,期望没烦恼啊。

posted @ 2021-10-08 15:59  Arextre  阅读(48)  评论(0编辑  收藏  举报