2024.1.2做题纪要

P4022 [CTSC2012] 熟悉的文章

单调队列“板子题”。

可以看出,要先对标准作文库建个广义 \(SAM\),对于答案的可能性,还是很明显是单调的。

该考虑怎么 \(check\),贪心??好像不太对,不让双 \(log\),所以只能 \(O(N)\)\(check\)。不能贪心,和数学也没啥关系,只剩下 \(dp\) 了。

考虑 \(dp\),我们设 \(dp[i]\) 表示枚举到第 \(i\) 位时,最长合法的匹配的位置有多少个,则存在转移式:

\[dp[i] = max\{dp[i - 1], max_{j = i - Len[i]}^{i - x} \{dp[j] + i - j \} \} \]

其中每个变量的含义见这个(其实我是褐的上面的链接的做法写的QAQ)

桃乃木かな
#include <bits/stdc++.h>

const int MAXN = 2e6 + 1000;

class Trie {
private:
    int root, tot;

public:
    int last[MAXN], number[MAXN];
    int child[MAXN][2];

    Trie() {
        root = tot = 1;
        last[root] = 1;
    }

    void Build(std::string &str) {
        int now = root;

        for (int i = 0; i < (int)str.size(); ++ i) {
            int ch = str[i] - '0';

            if (!child[now][ch]) {
                child[now][ch] = ++ tot;
                number[tot] = ch;
            }
            now = child[now][ch];
        }
    }
}trie;

class Suffix_Automaton {
private:
    int root, tot;

public:
    int child[MAXN][2];
    int link[MAXN], length[MAXN];

    Suffix_Automaton() {
        root = tot = 1;
    }

    int Insert(int c, int last) {
        int p = last, newP = ++ tot;
        
        length[newP] = length[p] + 1;
        while (p && !child[p][c]) {
            child[p][c] = newP;
            p = link[p];
        }
        if (!p) {
            link[newP] = root;
        }
        else {
            int q = child[p][c];

            if (length[q] == length[p] + 1) {
                link[newP] = q;
            }
            else {
                int newQ = ++ tot;

                memcpy(child[newQ], child[q], sizeof child[q]);
                length[newQ] = length[p] + 1;
                link[newQ] = link[q];
                link[q] = link[newP] = newQ;
                while (p && child[p][c] == q) {
                    child[p][c] = newQ;
                    p = link[p];
                }
            }
        }

        return newP;
    }
}suffixAutomaton;

void Bfs() {
    std::queue<std::pair<int, int>> queue;

    for (int i = 0; i <= 1; ++ i)
        if (trie.child[1][i])
            queue.emplace(1, trie.child[1][i]);
    while (queue.size()) {
        int father = queue.front().first;
        int now = queue.front().second;
        queue.pop();

        trie.last[now] = suffixAutomaton.Insert(trie.number[now], trie.last[father]);
        for (int i = 0; i <= 1; ++ i)
            if (trie.child[now][i])
                queue.emplace(now, trie.child[now][i]);
    }
}

int N, M, n;
std::string text;
int to[MAXN];

void Update() {
    int now = 1, len = 0;

    for (int i = 0; i < n; ++ i) {
        int ch = text[i] - '0';
        while (now && !suffixAutomaton.child[now][ch]) {
            now = suffixAutomaton.link[now];
            len = std::min(i + 1, suffixAutomaton.length[now]);
        }
        if (now) {
            len ++;
            to[i + 1] = i + 1 - len + 1;
            now = suffixAutomaton.child[now][ch];
        }
        else {
            now = 1;
        }
    }
}

class Monotonic_Queue {
private:
    int l, r;
    std::pair<int, int> number[MAXN / 2];

public:
    void Clear() {
        l = 1;
        r = 0;
    }

    void CheckRange(int L) {
        while (l <= r && number[l].first < L)
            l ++;
    }

    int Get() {
        if (l <= r)
            return number[l].second;
        else
            return -1e9;
    }

    void Insert(int pos, int value) {
        std::pair<int, int> add = std::make_pair(pos, value);
        while (l <= r && number[r].second <= value)
            r --;
        number[++ r] = add;
    }
}queue;

int dp[MAXN / 2];

int check(int x) {
    for (int i = 0; i <= n; ++ i) 
        dp[i] = 0;
    queue.Clear();

    for (int i = x; i <= n; ++ i) {
        queue.Insert(i - x, dp[i - x] - (i - x));
        queue.CheckRange(to[i] - 1);
        dp[i] = std::max(dp[i - 1], queue.Get() + i);
    }

    return dp[n];
}

int Answer() {
    int l = 1, r = n;
    int mid, result = 0;

    while (l <= r) {
        mid = (l + r) >> 1;
        if (10 * check(mid) >= 9 * n) {
            l = mid + 1;
            result = mid;
        }
        else {
            r = mid - 1;
        }
    }
    return result;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    std::cin >> N >> M;
    for (int i = 1; i <= M; ++ i) {
        std::cin >> text;
        trie.Build(text);
    }
    Bfs();
    for (int i = 1; i <= N; ++ i) {
        std::cin >> text;
        n = text.size();
        Update();
        std::cout << Answer() << '\n';
    }

    return 0;
}

P4770 [NOI2018] 你的名字

绝世好题,但是毒瘤。

先考虑当 \(l = 1, r = |S|\) 时,所以设 \(dp\) 式子就行了,这个 \(dp\) 式子很好啊。

剩下的可以用线段树合并维护 \(S\) 串的 \(parent\) 树的每个节点的 \(right(endpos)\) 集合。

更新在 \(T\) 串的每个位置的 \(length\) 的时候查询区间有没有数就行了。

具体见 https://www.luogu.com.cn/blog/stars/solution-p4770。写的很好。

みかみ ゆあ
#include <bits/stdc++.h>

const int MAXN = 3e6 + 100;
const int TREE = 40 * (1e6 + 100);

int range;

class Segment_Tree {
    #define lid (lson[id])
    #define rid (rson[id])

private:
    void Pushup(int id) {
        summary[id] = summary[lid] + summary[rid];
    }

public:
    int root[TREE], tot;
    int lson[TREE], rson[TREE];
    int summary[TREE];

    Segment_Tree() {
        tot = 0;
    }
    
    int Update(int id, int l, int r, int position) {
        if (!id)
            id = ++ tot;
        if (l == r) {
            summary[id] ++;
            return id;
        }
        
        int mid = (l + r) >> 1;
        
        if (position <= mid)
            lid = Update(lid, l, mid, position);
        else
            rid = Update(rid, mid + 1, r, position);
        Pushup(id);
        return id;
    }

    int Merge(int a, int b, int l, int r) {
        if (!a || !b) 
            return (a | b);

        int New = ++ tot;
        summary[New] = summary[a];

        if (l == r) {
            summary[New] += summary[b];
            return New;
        }
        
        int mid = (l + r) >> 1;
        
        lson[New] = Merge(lson[a], lson[b], l, mid);
        rson[New] = Merge(rson[a], rson[b], mid + 1, r);
        Pushup(New);
        return New;
    }

    bool Query(int id, int l, int r, int askL, int askR) {
        if (id == 0 || !summary[id])
            return false;
        if (askL <= l && r <= askR)
            return summary[id];
        
        int mid = (l + r) >> 1;
        bool result = false;
        
        if (askL <= mid)
            result |= Query(lid, l, mid, askL, askR);
        if (mid + 1 <= askR)
            result |= Query(rid, mid + 1, r, askL, askR);
        return result;
    }
}tree;

class Suffix_Automaton {
public:
    int root, last, tot;
    int child[MAXN][26];
    int link[MAXN], minPos[MAXN];;
    long long length[MAXN];

    Suffix_Automaton() {
        for (int i = 1; i <= 3e6 + 20; ++ i)
            minPos[i] = 1e9;
        root = tot = last = 1;
    }

    void Insert(int c, int pos, bool flag) {
        int p = last, newP = ++ tot;
        
        last = newP;
        length[newP] = length[p] + 1;
        if (flag == 0)
            tree.root[newP] = tree.Update(tree.root[newP], 1, range, pos);
        else
            minPos[newP] = pos;
        while (p && !child[p][c]) {
            child[p][c] = newP;
            p = link[p];
        }
        if (!p) {
            link[newP] = root;
        }
        else {
            int q = child[p][c];

            if (length[q] == length[p] + 1) {
                link[newP] = q;
            }
            else {
                int newQ = ++ tot;

                memcpy(child[newQ], child[q], sizeof child[q]);
                length[newQ] = length[p] + 1;
                link[newQ] = link[q];
                link[q] = link[newP] = newQ;
                while (p && child[p][c] == q) {
                    child[p][c] = newQ;
                    p = link[p];
                }
            }
        }
    }

    void Clear() {
        for (int i = 1; i <= tot; ++ i) {
            minPos[i] = 1e9;
            memset(child[i], 0, sizeof(child[i]));
        }
        root = tot = last = 1;
    }
}text, compare;

long long length[MAXN], answer;

void GetLength(const std::string &T, int rangeL, int rangeR) {
    int now = 1, len = 0;
    for (int i = 0; i < (int)T.size(); ++ i) {
        int ch = T[i] - 'a';
        while (true) {
            int next = text.child[now][ch];
            if (next != 0 && tree.Query(tree.root[next], 1, range, rangeL + len, rangeR)) {
                now = next;
                ++ len;
                break;
            }
            if (len == 0)
                break;
            -- len;
            if (len == text.length[text.link[now]])
                now = text.link[now];
        }
        length[i + 1] = len;
    }
}

int degree[MAXN];

void Topo(Suffix_Automaton &SAM, bool flag) {
    for (int i = 1; i <= SAM.tot; ++ i) 
        degree[SAM.link[i]] ++;

    std::queue<int> queue;

    for (int i = 1; i <= SAM.tot; ++ i)
        if (!degree[i])
            queue.emplace(i);
    while (queue.size()) {
        int now = queue.front();
        int to = SAM.link[now];
        queue.pop();

        degree[to] --;
        if (flag == false)
            tree.root[to] = tree.Merge(tree.root[to], tree.root[now], 1, range);
        else
            SAM.minPos[to] = std::min(SAM.minPos[to], SAM.minPos[now]);
        if (!degree[to] && to)
            queue.emplace(to);
    }
}

void GetAnswer() {
    answer = 0;
    for (int i = 2; i <= compare.tot; ++ i) {
        int father = compare.link[i];
        int minPosition = compare.minPos[i];
        answer += std::max(compare.length[i] - std::max(length[minPosition], compare.length[father]) , 0ll);
    }
}

int N;
std::string S, T;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    std::cin >> S >> N;
    range = S.size();
    for (int i = 0; i < (int)S.size(); ++ i) 
        text.Insert(S[i] - 'a', i + 1, 0);
    Topo(text, 0);
    for (int i = 1, l, r; i <= N; ++ i) {
        std::cin >> T >> l >> r;
        compare.Clear();
        for (int j = 0; j < (int)T.size(); ++ j)
            compare.Insert(T[j] - 'a', j + 1, 1);
        GetLength(T, l, r);
        Topo(compare, true);
        GetAnswer();
        std::cout << answer << '\n';
    }
}
/*
acb
1
caa
*/
posted @ 2024-01-02 11:11  觉清风  阅读(28)  评论(0编辑  收藏  举报