ABC346題解(F&G)

adam01發表於2024-03-23

\(a[l:r]\) 表示 \(a\) 這個 字串/陣列 的第 \(l\) 到第 \(r\) 個元素組成的 字串/陣列。類似的:link

F

考慮令 \(k=k0\),如何判斷是否可行。

只需要對 \(T\) 從前到後一個一個字元滿足,若無法滿足則不可行。

具體的,即假設現在判斷字元 \(c\),從第 \(q\) 次迴圈的字串 \(S\) 的第 \(p\) 位開始。

令還需要在 \(S\) 中找到 \(c\) 的個數為 \(b\)\(cnt_c\ s\) 表示 \(s\)\(c\) 的個數。

如果 \(b > cnt_c\ s\),那麼一直向後跳躍一整個字串(即 \(q\gets q + 1\)),將 \(b\) 減去 \(cnt_c\ s\)

考慮如果可以在 \(s[p:|S|]\) 這個字串裡滿足,那麼直接將 \(p\) 跳躍到某個 \(c\) 的下一個位置 \(p'\),滿足 \(cnt_c\ s[p:p']=b\) 即可。

否則令 \(b\gets b-cnt_c\ s[p:|S|]\),再令 \(q\gets q+1,p\gets 1\)

\(pos_c\ x\) 表示滿足 \(cnt_c\ s[1:i]=x\) 的最小的 \(i\)

\(p\gets (pos_c\ b)+1\) 即可。

若最後 \(q>n\),則不可行。

注意到答案顯然單調,二分答案即可。複雜度 \(O(|S|+|T|\log n)\)

G

看錯題卡了好久。注意是至少有一個滿足只出現一次而不是 恰好 有一個數只出現一次。

考慮從右向左掃描,若掃到 \(i\)

\(a[i:n]\)\(x\)\(i\) 次出現的位置為 \(pi_x\)

\(a[i:n]\) 可表示為 \(p1_{a_i},p1_{a_{i+1}},p2_{a_i}\cdots\)(只是一個例子)。

若將 \(p1_x\) 替換為 \(+1\)\(p2_x\) 替換為 \(-1\),其他為 \(0\),則 \(a[i:n]\) 變為 \(+1\ +1\ -1\ 0\cdots\)(只是一個例子)。

舉例:若 \(a[i:n]=\{1,2,3,2,3,4,1,1\}\)

第一次用 \(pi_x\) 替換為 \(\{p1_1,p1_2,p1_3,p2_2,p2_3,p1_4,p2_1,p3_1\}\)

接下來變為 \(\{+1,+1,+1,-1,-1,+1,-1,0\}\)

注意到做一個字首和後對於任意的 \(a[i:n]\) 沒有數 \(<0\),並且不為 \(0\) 的數的個數即為 \(L=i\)\(R\) 的方案數,考慮維護做字首和後的數列。

考慮類似於掃描線的方法,用線段樹維護區間最小值和最小值的個數,因為最小值 \(\ge 0\)(和掃描線類似),所以方便統計。

每次向左加一個數時只需要考慮 \(pi_x\) 的變化並且區間修改字首和就行了。複雜度 \(O(n\log n)\)

程式碼

F

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

#define int ll

const int N = 1e5 + 5, K = 26;

char s[N], t[N]; int n, sn, st;
int pos[N][K], suf[N][K], pre[N][K];
int cnt[26];

bool chk(int k)
{
    int p = 1, q = 1;
    for(int i = 1; i <= st; i ++)
    {
        int c = t[i] - 'a';
        if(!cnt[c]) return false;
        int k0 = k;
        if(k0 > cnt[c])
            q += (k0 - 1) / cnt[c], k0 = k0 - (k0 - 1) / cnt[c] * cnt[c];
        if(suf[p][c] < k0) k0 -= suf[p][c], p = 1, q ++;
        p = pos[pre[p - 1][c] + k0][c] + 1;
        if(p > sn) p = 1, q ++;
    }
    if(p == 1) q --;
    return q <= n;
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> s + 1 >> t + 1;
    sn = strlen(s + 1);
    st = strlen(t + 1);
    for(int i = sn; i >= 1; i --)
    {
        cnt[s[i] - 'a'] ++;
        for(int j = 0; j < K; j ++) suf[i][j] = cnt[j];
    }
    memset(cnt, 0, sizeof cnt);
    for(int i = 1; i <= sn; i ++)
    {
        cnt[s[i] - 'a'] ++;
        pos[cnt[s[i] - 'a']][s[i] - 'a'] = i;
        for(int j = 0; j < K; j ++) pre[i][j] = cnt[j];
    }
    int l = 0, r = n * sn / st;
    while(l < r)
    {
        int mid = l + r + 1 >> 1;
        if(chk(mid)) l = mid;
        else r = mid - 1;
    }
    cout << l;

    return 0;
}

G

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

#define int ll

const int N = 2e5 + 5;
int n, a[N];

int nxt[N], pos[N], ans;

struct sgt
{
    int a[N << 2], c[N << 2], tg[N << 2];
    void pu(int x)
    {
        a[x] = min(a[x << 1], a[x << 1 | 1]);
        c[x] = 0;
        if(a[x << 1] == a[x]) c[x] += c[x << 1];
        if(a[x << 1 | 1] == a[x]) c[x] += c[x << 1 | 1];
    }
    void pd(int x)
    {
        if(tg[x])
        {
            a[x << 1] += tg[x], a[x << 1 | 1] += tg[x];
            tg[x << 1] += tg[x], tg[x << 1 | 1] += tg[x];
            tg[x] = 0;
        }
    }
    void upd(int ql, int qr, int l, int r, int x, int v)
    {
        if(ql <= l && r <= qr)
        {
            a[x] += v, tg[x] += v;
            return;
        }
        int mid = l + r >> 1; pd(x);
        if(mid >= ql) upd(ql, qr, l, mid, x << 1, v);
        if(mid < qr) upd(ql, qr, mid + 1, r, x << 1 | 1, v);
        pu(x);
    }
    void build(int l, int r, int x)
    {
        if(l == r) return c[x] = 1, void();
        int mid = l + r >> 1;
        build(l, mid, x << 1);
        build(mid + 1, r, x << 1 | 1);
        pu(x);
        // cerr << c[x] << endl;
    }
}t;

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    // ans = n;
    t.build(1, n, 1);
    for(int i = n; i >= 1; i --)
    {
        if(!pos[a[i]]) nxt[i] = n + 1;
        else nxt[i] = pos[a[i]];
        pos[a[i]] = i;
    }
    for(int i = n; i >= 1; i --)
    {
        t.upd(i, n, 1, n, 1, 1);
        if(nxt[i] != n + 1)
        {
            if(nxt[nxt[i]] != n + 1)
            {
                t.upd(nxt[nxt[i]], n, 1, n, 1, 1);
            }
            t.upd(nxt[i], n, 1, n, 1, -2);
        }
        // cerr << t.c[1] << endl;
        if(t.a[1] == 0) ans += n - t.c[1];
        else ans += n;
    }
    cout << ans;

    return 0;
}