1.20 模拟赛口胡题解

幸亏 DP 都在前面签到,否则我又得寄

A

签到题。来点 O(n2) 正解。

k 做线段集为 {[l,r]|i=lrai=k}线段覆盖的结果最大值。

具体地,按 r 正序枚举 [l,r],考虑把 [l,r] 加入 k=i=lrai 的贡献。

#include <cstdio>
#include <algorithm>
using namespace std;
int n, q, a[1050], R[10000050], C[10000050];
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", a + i), a[i] += a[i - 1];
    for (int r = 1; r <= n; ++r)
        for (int l = r, v; l; --l)
            if (R[v = a[r] - a[l - 1]] < l)
                R[v] = r, q = max(q, ++C[v]);
    return !printf("%d", q);
}

Rvk=v 的子问题当前最右端点,Cvk=v 的子问题结果)

当然你谷神机已经无压力 1s 1e9 了,所以写个 O(n3) 大概率也是能过的(快进到 1e3 成为 O(n3) 共识

B

概率 DP 板子。

逆推:设 fi 为从 in 的期望,则有

fi=j=i+1min(i+6,n)fjmin(6,ni)+ai

正推:设 fi 为从 1i (不一定经过 i)的期望,则有

fi=j=max(i6,1)i1fj+aigjmin(6,nj)

其中 gi 为到达 i 的概率,且有

gi=j=max(i6,1)i1gjmin(6,nj)

觉得一堆 minmax 很烦可以把 DAG 建出来。

逆推:

#include <cstdio>
#include <algorithm>
using namespace std;
int n, a[1050];
double f[1050];
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", a + i);
    f[n] = a[n];
    for (int i = n - 1; i; --i)
    {
        for (int j = 1; j <= min(6, n - i); ++j)
            f[i] += f[i + j];
        f[i] = f[i] / min(6, n - i) + a[i];
    }
    printf("%.7lf", f[1]);
    return 0;
}

正推:

#include <cstdio>
#include <algorithm>
using namespace std;
int n, a[1050];
double f[1050], g[1050];
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", a + i);
    f[1] = a[1];
    g[1] = 1;
    for (int i = 2; i <= n; ++i)
        for (int j = 1; j <= min(6, i - 1); ++j)
            f[i] += (f[i - j] + a[i] * g[i - j]) / min(n - i + j, 6), g[i] += g[i - j] / min(n - i + j, 6);
    printf("%.7lf", f[n]);
    return 0;
}

C

树形 DP 板子。

fii 子树内权值和最大的子树的权值和,则有

fu=max{su,fv|vsonu}

其中 sii 子树权值和,且有

su=au+vsonusv

DP 过程中更新答案。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct E
{
    int v, t;
} e[200050];
int n, c, q = -2e9, s[100050], f[100050], h[100050];
void A(int u, int v)
{
    e[++c] = {v, h[u]};
    h[u] = c;
}
void D(int u, int k)
{
    for (int i = h[u], v; i; i = e[i].t)
        if ((v = e[i].v) != k)
            D(v, u), s[u] += s[v], q = max(q, f[u] + f[v]), f[u] = max(f[u], f[v]);
    f[u] = max(f[u], s[u]);
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", s + i);
    for (int i = 1, u, v; i < n; ++i)
        scanf("%d%d", &u, &v), A(u, v), A(v, u);
    memset(f, 0xc0, sizeof f);
    D(1, 0);
    if (q < -1e9)
        puts("Impossible");
    else
        printf("%d", q);
    return 0;
}

D

猜结论。

结论:DAG 加若干条不定向的边一定能组成 DAG。

总的反转代价是由所有需要反转的边中,代价最高的那一条决定的,求达成目标的最小代价。

最大值最小,二分。

二分代价 k,则边权 k 的边可以任意反转,若边权 >k 的边组成 DAG,则此 DAG 加入边权 k 的不定向边一定能组成 DAG,则此代价 k 合法。

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
struct S
{
    int u, v, w;
} s[100050];
struct E
{
    int v, t;
} e[100050];
queue<int> q;
int n, m, c, L, R = 1e9, M, d[100050], h[100050];
void A(int u, int v)
{
    ++d[v];
    e[++c] = {v, h[u]};
    h[u] = c;
}
bool C(int k)
{
    int r = 0;
    memset(e, 0, sizeof e);
    memset(d, 0, sizeof d);
    memset(h, c = 0, sizeof h);
    for (int i = 0; i < m; ++i)
        if (s[i].w > k)
            A(s[i].u, s[i].v);
    for (int i = 1; i <= n; ++i)
        if (!d[i])
            q.push(i);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        ++r;
        for (int i = h[u], v; i; i = e[i].t)
            if (!--d[v = e[i].v])
                q.push(v);
    }
    return r == n;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; ++i)
        scanf("%d%d%d", &s[i].u, &s[i].v, &s[i].w);
    while (L <= R)
        if (C(M = L + R >> 1))
            R = M - 1;
        else
            L = M + 1;
    printf("%d", L);
    return 0;
}

E

DS。

赛时看到换根想 LCT,然后发现 LCT 不好维护子树信息,现学 ETT 换根失败,Top Tree 又不会写(

当然拿上面提到的动态树都是可以嗯做这题的,考虑容易的做法。

找规律。(在拿 DFS 序嗯造 ETT 的时候找到的,不知道 zq 什么做法

考虑线段树维护 DFS 序,发现只有换根操作不好维护。

分类讨论查询 x1,x,root 的位置关系。

  1. x=root:全局最小值。
  2. x 不在 1root 路径上:以 1 为根意义下 x 子树最小值。
  3. x1root 路径上:设 Zrootdeprootdepx1 级祖先的子树,答案为 UZ 的最小值。

(辅助理解第三种情况)

树上倍增维护 k 级祖先,你要写长剖我不拦你。

#include <cstdio>
#include <algorithm>
#define G int m = p->s + p->t >> 1
using namespace std;
struct E
{
    int v, t;
} e[200050];
int n, m, c, g, R, a[100050], b[100050], k[100050], d[100050], s[100050], h[100050], f[100050][20];
char o;
void A(int u, int v)
{
    e[++c] = {v, h[u]};
    h[u] = c;
}
void D(int u)
{
    s[k[b[u] = ++g] = u] = 1;
    for (int i = h[u], v; i; i = e[i].t)
        if (!d[v = e[i].v])
        {
            d[v] = d[u] + 1;
            f[v][0] = u;
            for (int j = 1; f[v][j - 1]; ++j)
                f[v][j] = f[f[v][j - 1]][j - 1];
            D(v);
            s[u] += s[v];
        }
}
struct T
{
    T *l, *r;
    int s, t, v;
    T(int s, int t) : s(s), t(t) {}
    void u() { v = min(l->v, r->v); }
} * r;
void B(int s, int t, T *&p)
{
    p = new T(s, t);
    if (s == t)
        return void(p->v = a[k[s]]);
    G;
    B(s, m, p->l);
    B(m + 1, t, p->r);
    p->u();
}
void M(int l, int k, T *p)
{
    if (p->s == p->t)
        return void(p->v = k);
    G;
    if (l <= m)
        M(l, k, p->l);
    else
        M(l, k, p->r);
    p->u();
}
int Q(int l, int r, T *p)
{
    if (l <= p->s && p->t <= r)
        return p->v;
    G;
    int q = 1e9;
    if (l <= m)
        q = min(q, Q(l, r, p->l));
    if (r > m)
        q = min(q, Q(l, r, p->r));
    return q;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = R = 1, x; i <= n; ++i)
    {
        scanf("%d%d", &x, a + i);
        if (x)
            A(x, i), A(i, x);
    }
    D(d[1] = 1);
    B(1, n, r);
    for (int i = 0, x, y, z, q; i < m; ++i)
    {
        scanf(" %c%d", &o, &x);
        switch (o)
        {
        case 'V':
            scanf("%d", &y);
            M(b[x], y, r);
            break;
        case 'E':
            R = x;
            break;
        case 'Q':
            if (x == R)
                printf("%d\n", r->v);
            else
            {
                z = R;
                for (int k = 0; k <= 30; ++k)
                    if (d[R] - d[x] - 1 & 1 << k)
                        z = f[z][k];
                if (f[z][0] != x)
                    printf("%d\n", Q(b[x], b[x] + s[x] - 1, r));
                else
                {
                    q = Q(1, b[z] - 1, r);
                    if (b[z] + s[z] <= n)
                        q = min(q, Q(b[z] + s[z], n, r));
                    printf("%d\n", q);
                }
            }
        }
    }
    return 0;
}

F

不会。

posted @   Jijidawang  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示