1. 程式人生 > 其它 >[噼昂!]game(Pending)

[噼昂!]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;
}

肆、關鍵 の 地方 ¶

其實仔細想一想,許多概率期望題的本質都是計數問題,所以,組合學得好,期望沒煩惱啊。