「Fortuna OJ」Jul 11th – Group B 解题报告

A – 照片

这道题是一道很考察思维的题目,我在这里介绍 DP 的做法。

我们考虑 DP。大概的方程可以写成:

\[ dp[i] = max\{ dp[j], j \in S_i \} + 1 \]

其中,\(dp[i]\)代表\([1, i]\)之间最多的斑点奶牛数量,然后\(S_i\)就是我们可以转移来的区间。我们可以提前处理好每一个点对应的\(lft_{S_i}, rig_{S_i}\),也就是每一个点集合的左右端点。这个区间满足一个根本的条件:这个区间是点\(i\)左边最近的、不包含\(i\)的集合。我们肯定可以从这一段区间搞出最大的答案。预处理的时候先默认\(rgt[i] = i – 1\),最后做个小 DP 更新数据即可。

现在考虑优化这个 DP,毕竟求出了 \(S_i\) 的范围也只是常数上的优化,我们现在来卡指数。发现连续的\(\dots, S_{i-1}, S_i, S_{i+1}, \dots\)之间一般(存在无解的情况)也是连续的,所以我们在用单调队列的时候也可以连续的来维护,注意队头的下标距离和队尾的斜率处理。

// A.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 201000;

int dp[MAX_N], lft[MAX_N], rig[MAX_N], n, m, q[MAX_N << 1];

int main()
{
    scanf("%d%d", &n, &m);

    for (int i = 1; i <= n + 1; i++)
        rig[i] = i - 1;

    for (int i = 1, l, r; i <= m; i++)
        scanf("%d%d", &l, &r), rig[r] = min(rig[r], l - 1), lft[r + 1] = max(lft[r + 1], l);
    for (int i = n; i >= 1; i--)
        rig[i] = min(rig[i], rig[i + 1]);
    for (int i = 2; i <= n + 1; i++)
        lft[i] = max(lft[i], lft[i - 1]);
    int l = 1, r = 1, j = 1;
    q[1] = 0;
    for (int i = 1; i <= n + 1; i++)
    {
        while (j <= min(rig[i], n))
        {
            if (dp[j] == -1)
            {
                j++;
                continue;
            }
            while (l <= r && dp[j] > dp[q[r]])
                r--;
            q[++r] = j, j++;
        }
        while (l <= r && q[l] < lft[i])
            l++;
        if (l <= r)
            dp[i] = dp[q[l]] + ((i != n + 1) ? 1 : 0);
        else
            dp[i] = -1;
    }
    printf("%d", dp[n + 1]);
    return 0;
}

B – 阴阳

这道题我在考场上看出来了是 sb 点分治模版题,但是打挂了,说明还是不够熟练,需要多加练习。

考虑点分治的时候,从根出发的链会提供两种信息:链权和与有无零点。题目要求我们记录路径的时候判断是否有零点。所以,记录答案的时候一个是要特判链权和为零的情况,且要记录链权的同时要记录有无原点。搞清楚了这两件事情,点分治即可。

// B.cpp
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int MAX_N = 101000;

int head[MAX_N], current, siz[MAX_N], n, dep[MAX_N], dist[MAX_N], mxdep;
int root_val, root, current_stat[MAX_N];
ll f[MAX_N << 1][2], g[MAX_N << 1][2], answer;
bool tag[MAX_N];

struct edge
{
    int to, nxt, weight;
} edges[MAX_N << 1];

void addpath(int src, int dst, int weight)
{
    edges[current].nxt = head[src], edges[current].weight = weight;
    edges[current].to = dst, head[src] = current++;
}

void __find_root(int u, int fa, int capacity)
{
    int mx_val = 0;
    siz[u] = 1;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa && !tag[edges[i].to])
            __find_root(edges[i].to, u, capacity), siz[u] += siz[edges[i].to], mx_val = max(mx_val, siz[edges[i].to]);
    mx_val = max(mx_val, capacity - siz[u]);
    if (mx_val < root_val)
        root_val = mx_val, root = u;
}

int find_root(int u, int capacity)
{
    root_val = capacity, root = 0;
    __find_root(u, 0, capacity);
    return root;
}

void dfs1(int u, int fa, int dep, int way)
{
    mxdep = max(mxdep, dep);
    if (current_stat[way] > 0)
        f[way][1]++;
    else
        f[way][0]++;
    current_stat[way]++;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa && !tag[edges[i].to])
            dfs1(edges[i].to, u, dep + 1, way + edges[i].weight);
    current_stat[way]--;
}

void dq(int u, int capacity)
{
    int mx = 0;
    tag[u] = true, g[MAX_N][0] = 1;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (!tag[edges[i].to])
        {
            mxdep = 1;
            dfs1(edges[i].to, u, 1, MAX_N + edges[i].weight);
            mx = max(mx, mxdep);
            answer += (g[MAX_N][0] - 1) * f[MAX_N][0];
            for (int d = -mxdep; d <= mxdep; d++)
                answer += (g[MAX_N - d][1] * f[MAX_N + d][1]) + (g[MAX_N - d][0] * f[MAX_N + d][1]) + (g[MAX_N - d][1] * f[MAX_N + d][0]);
            for (int d = MAX_N - mxdep; d <= MAX_N + mxdep; d++)
            {
                g[d][0] += f[d][0];
                g[d][1] += f[d][1];
                f[d][0] = f[d][1] = 0;
            }
        }
    for (int d = MAX_N - mx; d <= MAX_N + mx; d++)
        g[d][0] = g[d][1] = 0;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (!tag[edges[i].to])
        {
            int capacity_ = siz[edges[i].to];
            dq(find_root(edges[i].to, capacity_), capacity_);
        }
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d", &n);
    for (int i = 1, u, v, len; i <= n - 1; i++)
        scanf("%d%d%d", &u, &v, &len), addpath(u, v, len == 0 ? -1 : 1), addpath(v, u, len == 0 ? -1 : 1);
    dq(find_root(1, n), n);
    printf("%lld", answer);
    return 0;
}

C – 数字八

这道题就是一道 DP 题。懒得讲了,

Leave a Reply

Your email address will not be published. Required fields are marked *