2018冬令营模拟测试赛(六)

本文提供了2018年冬令营模拟测试赛的三道题目解答,包括字符串前缀树问题、基于深度维护信息的树形结构问题以及贪心算法结合霍尔定理的应用问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2018冬令营模拟测试赛(六)

[Problem A]string

试题描述

TAT

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

题解

一口一个前缀,二话不说建立 trie 树。

为了避免算重,我们在统计一个串的时候要求在最后的分割位置统计它,即不存在更靠后的分割位置。为了达到这一点我们先枚举后半部分,由于后半部分是某个串的一个前缀,即 trie 树上从根到某个点 \(u\) 的一条路径,那么现在就是要求前面填的串满足一个性质。

\(v = fail[u]\),即失配边指向的位置,\(w\) 是根节点到 \(u\) 的路径中刨去根节点到 \(v\) 的路径这个后缀的串所对应的节点(也即 \(u\) 在 trie 树上的 \(k\) 级祖先,\(k\) 为根到 \(v\) 的路径长度)。容易发现只要前面填的串不是 \(v\)失配树中的子树中的节点就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 1000010
#define maxa 30
#define maxlog 20
#define LL long long

char str[maxn];
int ToT, ch[maxn][maxa], dep[maxn], pa[maxn][maxlog];

void Insert(char *s) {
    int n = strlen(s), u = 1;
    rep(i, 0, n - 1) {
        int x = s[i] - 'a';
        if(!ch[u][x]) ch[u][x] = ++ToT;
        u = ch[u][x];
    }
    return ;
}
void build(int u) {
    rep(i, 1, maxlog - 1) pa[u][i] = pa[pa[u][i-1]][i-1];
    rep(c, 0, maxa - 1) if(ch[u][c]) {
        pa[ch[u][c]][0] = u;
        dep[ch[u][c]] = dep[u] + 1;
        build(ch[u][c]);
    }
    return ;
}
int KthPar(int u, int k) {
    rep(i, 0, maxlog - 1) if(k >> i & 1) u = pa[u][i];
    return u;
}
int fa[maxn], tofa[maxn], siz[maxn], Q[maxn], hd, tl;
void getfail() {
    hd = tl = 0; Q[++tl] = 1;
    while(hd < tl) {
        int u = Q[++hd];
        rep(c, 0, maxa - 1) if(ch[u][c]) {
            int v = ch[u][c], j = fa[u];
            while(j && !ch[j][c]) j = fa[j];
            fa[v] = ch[j][c] ? ch[j][c] : 1;
            if(fa[v] > 1) tofa[v] = KthPar(v, dep[fa[v]]);
            Q[++tl] = v;
        }
    }
    dwn(i, tl, 1) {
        int u = Q[i];
        siz[u]++; siz[fa[u]] += siz[u];
    }
    return ;
}

int main() {
    read();
    int n = read();
    ToT = 1;
    rep(i, 1, n) scanf("%s", str), Insert(str);
    build(1);
    getfail();
    
    LL ans = 0; siz[0] = 0;
    rep(i, 2, ToT) ans += ToT - siz[tofa[i]] - !tofa[i]; // (ToT - 1) - (siz[tofa[i]] - 1)
    printf("%lld\n", ans);
    
    return 0;
}

[Problem B]tree

试题描述

QAQ

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

见“试题描述

题解

这是一道可以转化为基于深度维护信息的题,所以做法是长链剖分。

\(f(i, j)\) 表示节点 \(i\) 的子树中,还需要向子树 \(i\) 外部延伸 \(j\) 的距离能够组成的三元组的点对的贡献(这只是一个状态,具体计算还需要维护 \(\sum a_0 + a_1\)\(\sum a_0a_1\))。

在合并的时候我们可以暴力扫描一下其他子树的短链,然后把子树之间的贡献利用 \(f(i, j)\) 计算,同时合并更新 \(f(i, j)\)。在重链上上移的时候,需要数组整体移位,这个东西记录一个移了多少位的变量就行了;在每一步移道位置 \(0\)(即 \(j = 0\) 那一位)时 \(f(i, j)\) 和当前节点也可以组成三元组,记得累计一下贡献。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 100010
#define maxm 200010
#define MOD 998244353
#define LL long long

int n, m, head[maxn], nxt[maxm], to[maxm], val[maxn];

void AddEdge(int a, int b) {
    to[++m] = b; nxt[m] = head[a]; head[a] = m;
    swap(a, b);
    to[++m] = b; nxt[m] = head[a]; head[a] = m;
    return ;
}

#define pll pair <LL, LL>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)
vector <pll> info[maxn]; // x: _sum{a0+a1}, y: _sum{a0*a1}
int mxd[maxn], son[maxn], dep[maxn], clo, dfn[maxn], uid[maxn], top[maxn];
void build(int u, int fa) {
    for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
        dep[to[e]] = dep[u] + 1;
        build(to[e], u);
        if(!son[u] || mxd[son[u]] < mxd[to[e]]) son[u] = to[e];
    }
    mxd[u] = max(mxd[son[u]], dep[u]);
    return ;
}
void gett(int u, int tp, int fa) {
    if(u == tp) info[u].resize(mxd[u] - dep[u] + 3 << 1);
    uid[dfn[u] = ++clo] = u; top[u] = tp;
    if(son[u]) gett(son[u], tp, u);
    for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa && to[e] != son[u]) gett(to[e], to[e], u);
    return ;
}

LL ans, sumv[maxn], cntv[maxn];
void solve(int u, int fa) {
    if(son[u]) solve(son[u], u);
    int minus = mxd[u] - dep[u];
    // printf("solve(%d)\n", u);
    for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa && to[e] != son[u]) {
        solve(to[e], u);
        // printf("merge %d and %d\n", u, to[e]);
        rep(h, 0, mxd[to[e]] - dep[to[e]]) {
            // printf("for height %d\n", h);
            int i = h + 1 + minus, j = dfn[to[e]] + h;
            ans += info[top[u]][i].x * sumv[j] % MOD;
            ans += info[top[u]][i].y * cntv[j] % MOD;
            // printf("%d and %d: %lld * %lld + %lld * %lld = %lld\n", u, to[e], info[top[u]][i].x, sumv[j], info[top[u]][i].y, cntv[j], info[top[u]][i].x * sumv[j] + info[top[u]][i].y * cntv[j]);;
            i = dfn[u] + h; j = h + 1 + mxd[to[e]] - dep[to[e]];
            ans += sumv[i] * info[to[e]][j].x % MOD;
            ans += cntv[i] * info[to[e]][j].y % MOD;
            // printf("also: %lld * %lld + %lld * %lld = %lld\n", sumv[i], info[to[e]][j].x, cntv[i], info[to[e]][j].y, sumv[i] * info[to[e]][j].x + cntv[i] * info[to[e]][j].y);
            ans %= MOD;
        }
        (info[top[u]][minus].x += info[to[e]][mxd[to[e]]-dep[to[e]]+1].x) %= MOD;
        (info[top[u]][minus].y += info[to[e]][mxd[to[e]]-dep[to[e]]+1].y) %= MOD;
        rep(h, 1, mxd[to[e]] - dep[to[e]] + 1) {
            int i = h + minus, j = h + 1 + mxd[to[e]] - dep[to[e]], di = dfn[u] + h, dj = dfn[to[e]] + h - 1;
            info[top[u]][i].x += sumv[di] * cntv[dj] % MOD + sumv[dj] * cntv[di] % MOD;
            info[top[u]][i].y += sumv[di] * sumv[dj] % MOD;
            (info[top[u]][i].x += info[to[e]][j].x) %= MOD;
            // printf("+= %lld\n", info[to[e]][j].x);
            (info[top[u]][i].y += info[to[e]][j].y) %= MOD;
            (sumv[di] += sumv[dj]) %= MOD; (cntv[di] += cntv[dj]) %= MOD;
            // printf("for height %d\nmerging: (a0+a1)%lld (a0*a1)%lld (sumv)%lld (cntv)%lld\n", h, info[top[u]][i].x, info[top[u]][i].y, sumv[di], cntv[di]);
        }
    }
    sumv[dfn[u]] = val[u]; cntv[dfn[u]] = 1;
    (ans += info[top[u]][minus].x * val[u] % MOD + info[top[u]][minus].y) %= MOD;
    // printf("ans += %lld * %lld + %lld = %lld\n", info[top[u]][minus].x, val[u], info[top[u]][minus].y, info[top[u]][minus].x * val[u] + info[top[u]][minus].y);
    return ;
}

int main() {
    read();
    n = read();
    rep(i, 1, n - 1) {
        int a = read(), b = read();
        AddEdge(a, b);
    }
    rep(i, 1, n) val[i] = read();
    
    build(1, 0);
    gett(1, 1, 0);
    /*rep(u, 1, n) {
        printf("[%d] top, son: %d, %d\n", u, top[u], son[u]);
        if(top[u] == u) printf("vector_size: %d\n", info[u].size());
    } // */
    
    solve(1, 0);
    
    printf("%lld\n", ans);
    
    return 0;
}

[Problem C]heike

试题描述

QwQ

ToT

输入

见“试题描述

输出

见“试题描述

输入示例

见“试题描述

输出示例

见“试题描述

数据规模及约定

注意:本题给出的数据范围是假的,应为 \(n,m \le 10^6\)

题解

这题先要证明一下(略过)是个拟阵,但是我觉得考虑替换也可以意识到它是个贪心了。

然后就是要上霍尔定理,这样我们可以得到:一个区间是满的当且仅当它所包含的 boys 总数等于区间长度。

每加入一个 boy 的时候维护一下那些区间满了,每个区间包含它的满区间最短的是谁(由于“满”有可交、可并性,证明略,故这个是可以求的),这个可以在每次加入成功时 dp 一下。

顺便维护一下一个区间中包含的最小权值的 boy,用于“假插入”询问。

对于没加入的 boy,在包含它的最短满区间上打个标记,表示它是备选的,到时候如果某个 boy 被删了导致那个区间不满,就可以把这个 boy 加进去。

最后把上面说的那个标记再 dp 下传一下,这样删掉一个 boy 就可以 \(O(1)\) 求出替补是谁。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 310
#define maxm 1000010
#define oo 2147483647
#define LL long long
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)

struct Boy {
    int l, r, v, id;
    Boy() {}
    Boy(int _1, int _2, int _3, int _4): l(_1), r(_2), v(_3), id(_4) {}
    bool operator < (const Boy& t) const { return v > t.v; }
} bs[maxm];
bool used[maxm];
int n, real[maxm], cb, q, tot[maxn][maxn], inMin[maxn][maxn], getMax[maxn][maxn];
pii Full[maxn][maxn];

void upd(pii& a, pii b) {
    if(!b.x) return ;
    if(!a.x || a.y - a.x + 1 > b.y - b.x + 1) a = b;
    return ;
}

int main() {
    read();
    n = read(); cb = read(); q = read();
    rep(i, 1, cb) {
        int l = read(), r = read(), v = read();
        bs[i] = Boy(l, r, v, i);
    }
    
    rep(i, 1, n) rep(j, i, n) inMin[i][j] = oo;
    LL ans = 0;
    sort(bs + 1, bs + cb + 1);
    rep(i, 1, cb) real[bs[i].id] = i;
    rep(i, 1, cb) {
        Boy& b = bs[i];
        if(!Full[b.l][b.r].x) {
            used[i] = 1;
            ans += b.v;
            rep(l, 1, b.l) rep(r, b.r, n) tot[l][r]++, inMin[l][r] = min(inMin[l][r], b.v);
            dwn(len, n, 1) rep(l, 1, n - len + 1) {
                int r = l + len - 1;
                if(tot[l][r] == r - l + 1) upd(Full[l][r], pii(l, r));
                if(len > 1) upd(Full[l][r-1], Full[l][r]), upd(Full[l+1][r], Full[l][r]);
            }
        }
        else {
            pii& fu = Full[b.l][b.r];
            getMax[fu.x][fu.y] = max(getMax[fu.x][fu.y], b.v);
        }
    }
    dwn(len, n, 2) rep(l, 1, n - len + 1) {
        int r = l + len - 1, now = getMax[l][r];
        getMax[l][r-1] = max(getMax[l][r-1], now);
        getMax[l+1][r] = max(getMax[l+1][r], now);
    }
    
    printf("%lld\n", ans);
    while(q--) {
        int tp = read();
        if(tp == 1) {
            int nl = read(), nr = read(), nv = read();
            pii& fu = Full[nl][nr];
            printf("%lld\n", ans - inMin[fu.x][fu.y] + max(inMin[fu.x][fu.y], nv));
        }
        if(tp == 2) {
            int i = real[read()];
            if(!used[i]){ printf("%lld\n", ans); continue; }
            printf("%lld\n", ans - bs[i].v + getMax[bs[i].l][bs[i].r]);
        }
    }
    
    return 0;
}

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8177736.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值