2021牛客多校10 Browser Games(压缩trie)

银川K加强版,内存限制32M,字符串总长1e5*100,直接建出trie树dp很显然是会T的。
dp的思路:每个串都对应一个叶子节点,每次都是以某个叶子为起点向上dp。每个节点的权值等于这棵子树还有多少个叶子节点(即这个点有多少个串经过),以这个为每个点的权值。按顺序从叶子节点开始删,每次先让ans++,然后沿着叶子向上删到不是叶子节点(并且不是根子节点的点),减掉这些点的dp值,最后在把最后删的点的父节点的dp值++。

从上面这个dp的过程中可以发现,dp只需要记录开始的叶子节点,并不需要再去匹配,所以只需要树结构本身,而不需要每个点对应的字符;同时串之间会共用很多节点,如果把这些节点缩成一个点,空间占用就能很大程度的减小。

那么我们可以把串按字典序排序,那些共用节点的串肯定有公共前缀,这些前缀的长度各不相同,可以分治建出树来dp。

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define ll long long
//#define double long double
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
#define pii pair<int,int>
#define pll pair<ll,ll>
const double PI = acos(-1);
const int inf = 1e9;
const ll lnf = 1e18 + 7;
const int maxn = 3e5 + 10;
const int N = 1 << 30 - 1;
ll mod = 998244353;
double eps = 1e-8;

int fa[maxn];//父节点
int dp[maxn];
int v[maxn];//每个点的子树中有多少叶子
int cnt = 0;

char c[maxn][105];//串
int id[maxn];//排序后的下标

//分治建树
void make_tree(int l, int r, int deep, int root)
{
    //先对当前分治的区间[l,r]排序(它们[0,deep)的前缀相同)
    sort(id + l, id + r + 1, [&](int x, int y) {
        return c[x][deep] < c[y][deep];
        });
    int i = l, j = l;
    while (i <= r)
    {
        if (i + 1 > r || c[id[i]][deep] != c[id[i + 1]][deep])//找到一些c[deep]相同的串
        {
            //cout << now << endl;
            if (i == j)//如果只有1个串,那么它将会是叶子,直接建出来
            {
                fa[id[i]] = root;
                v[id[i]] = 1;
                i++, j++;
                continue;
            }
            //否则去看这些共c[deep]的串的c[deep+1]、c[deep+2]是否可以缩成1个点
            int nex = 1;
            while (1)
            {
                bool ok = 1;
                for (int k = j; ok && k < i; k++)
                    if (c[id[k]][deep + nex] != c[id[k + 1]][deep + nex])
                        ok = 0;
                if (ok)
                    nex++;
                else
                {
                    ++cnt;
                    v[cnt] = i - j + 1;
                    fa[cnt] = root;
                    make_tree(j, i, deep + nex, cnt);
                    j = i + 1;
                    break;
                }
            }
        }
        i++;
    }
}

int main()
{
    int n;
    scanf("%d", &n);
    cnt = n;
    for (int i = 1; i <= n; i++)
        scanf("%s", c[i] + 1), id[i] = i;
    make_tree(1, n, 1, 0);
    int ans = 0;
    vector<int>dp(cnt + 1, 0);
    for (int x = 1; x <= n; x++)
    {
        int now = x, p = -1;
        while (now)
        {
            v[now]--;
            if (v[now] == 0)
                p = now, ans -= dp[now];
            now = fa[now];
        }
        dp[fa[p]]++;
        ans++;
        printf("%d
", ans);
    }
    return 0;

}
原文地址:https://www.cnblogs.com/ruanbaiQAQ/p/15158026.html