列表

详情


NC20594. [SHOI2017]摧毁“树状图”

描述

自从上次神刀手帮助蚯蚓国增添了上千万人口(蚯口?),蚯蚓国发展得越来越繁荣了!最近,他们在地下发现了 一些神奇的纸张,经过仔细研究,居然是D国X市的超级计算机设计图纸!这台计算机叫做‘树状图’,由n个计算 节点与n1条可以双向通信的网线连接而成,所有计算节点用不超过n的正整数编号。顾名思义,这形成了一棵树的 结构。蚯蚓国王已在图纸上掌握了这棵树的完整信息,包括n的值与n1条网线的连接信息。
于是蚯蚓国王决定,派 出蚯蚓国最强大的两个黑客,小P和小H,入侵‘‘树状图’’,尽可能地摧毁它。小P和小H精通世界上最好的编程 语言,经过一番商量后,他们决定依次采取如下的步骤:小P选择某个计算节点,作为他入侵的起始点,并在该节 点上添加一个P标记。重复以下操作若干次(可以是0次):–小P从他当前所在的计算节点出发,选择一条没有被 标记过的网线,入侵到该网线的另一端的计算节点,并在路过的网线与目的计算节点上均添加一个P标记。小H选择 某个计算节点,作为她入侵的起始点,并在该节点上添加一个H标记。重复以下操作若干次(可以是0次):–小H 从她当前所在的计算节点出发,选择一条没有被标记过的网线,入侵到该网线的另一端的计算节点,并在路过的网 线与目的计算节点上均添加一个H标记。(注意,小H不能经过带有P标记的网线,但是可以经过带有P标记的计算节 点)删除所有被标记过的计算节点和网线。对于剩下的每条网线,如果其一端或两端的计算节点在上一步被删除了 ,则也删除这条网线。经过以上操作后,‘‘树状图’’会被断开,剩下若干个(可能是0个)连通块。为了达到 摧毁的目的,蚯蚓国王希望,连通块的个数越多越好。于是他找到了你,希望你能帮他计算这个最多的个数。
小P 和小H非常心急,在你计算方案之前,他们可能就已经算好了最优方案或最优方案的一部分。你能得到一个值x:若 x=0,则说明小P和小H没有算好最优方案,你需要确定他们两个的入侵路线。若x=1,则说明小P已经算好了某种两 人合作的最优方案中,他的入侵路线。他将选择初始点p0,并沿着网线一路入侵到了目标点p1,并且他不会再沿着 网线入侵;你只需要确定小H的入侵路线。若x=2,则说明小P和小H算好了一种两人合作的最优方案,小P从点p0入 侵到了p1并停下,小H从点h0入侵到了h1并停下。此时你不需要指挥他们入侵了,只需要计算最后两步删除计算节 点与网线后,剩下的连通块个数即可。

输入描述

每个输入文件包含多个输入数据。输入文件的第一行为两个整数 T 和 x, T 表示该文件包含的输入数据个数, x 的含义见上述。(同一个输入文件的所有数据的 x 都是相同的) 
接下来依次输入每个数据。
每个数据的第一行有若干个整数: 
若 x = 0,则该行只有一个整数 n。 
若 x = 1,则该行依次有三个整数 n, p0, p1。 
若 x = 2,则该行依次有五个整数 n, p0, p1, h0, h1。保证 p0, p1, h0, h1 均为不超过 n 的正整数。
每个数据接下来有 n 1 行,每行有两个不超过 n 的正整数,表示这两个编号的计算节点之间有一条网线将其相连。保证输入的是一棵树。同一行相邻的整数之间用恰好一个空格隔开。 
对于整数 k,设 ∑ n^k 为某个输入文件中,其 T 个输入数据的 n^k 之和。
所有输入文件满足 T ≤ 10^5, ∑ n^1 ≤ 5 × 10^5。 
数据文件可能较大,请避免使用过慢的输入输出方法。

输出描述

对于每个数据,输出一行,表示在给定条件下,剩下连通块的最大个数。

示例1

输入:

1 0
13
1 2
2 3
2 4
4 5
4 6
4 7
7 8
7 9
9 10
10 11
10 12
12 13

输出:

8

说明:

这个输入文件只有一个输入数据。一种最优的方案如下:
小 P 从节点 2 开始入侵,节点 2 被小 P 标记。
小 P 从节点 2 入侵到节点 4,节点 4 和经过的网线被小 P 标记。
小 P 从节点 4 入侵到节点 7,节点 7 和经过的网线被小 P 标记。
小 H 从节点 10 开始入侵,节点 10 被小 H 标记。
删除被标记的节点 2,4,7,10 和被标记的网线 (2,4) 和 (4,7)。
删除任意一端在上一步被删除的网线。
此时还剩下 8 个连通块。其中节点 1,3,5,6,8,9,11 各自形成一个连通块,节点 12,13
形成了一个连通块。

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(clang++11) 解法, 执行用时: 150ms, 内存消耗: 9528K, 提交时间: 2020-11-14 10:52:03

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
inline int readInt() {
    static int n, ch;
    n = 0, ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) n = n * 10 + ch - '0', ch = getchar();
    return n;
}

const int MAX_N = 100000 + 3, INF = 0x0f0f0f0f;
int x, n, p0, p1, h0, h1;

struct Edge {
    Edge *next;
    int to;
    Edge(Edge *next = NULL, int to = 0) : next(next), to(to) {}
} pool[MAX_N * 2], *pit = pool, *first[MAX_N];

int f[MAX_N][6], cnt[MAX_N];

void dfs(int u, int fa) {
    cnt[u] = -1;
    for (Edge *e = first[u]; e; e = e->next)
        if (e->to != fa) ++cnt[u], dfs(e->to, u);
}

inline void tension(int &a, const int &b) {
    if (b > a) a = b;
}

void dp(int u, int fa) {
    memset(f[u], -0x3f, sizeof f[u]);
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) dp(e->to, u);

    // f[u][0] 向上
    f[u][0] = cnt[u]; // 单独 
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa)
        tension(f[u][0], f[e->to][0] + cnt[u]);

    // f[u][1] 有一个完整的,不包含 u
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa)
        tension(f[u][1], f[e->to][2] + 1), tension(f[u][1], f[e->to][1]);

    // f[u][2] 有一个完整的,包含 u
    int mx1 = 0;
    f[u][2] = cnt[u] + 1;
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) {
        tension(f[u][2], mx1 + f[e->to][0] + cnt[u] + 1);
        tension(mx1, f[e->to][0]);
    }

    // f[u][3] 有一个完整的,且向上
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) // 继承
        tension(f[u][3], f[e->to][3] + cnt[u]);

    // 完整的在下面
    int mx2 = 0;
    mx1 = -INF;
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) {
        tension(f[u][3], mx1 + f[e->to][0] + cnt[u]);
        tension(f[u][3], f[e->to][1] - 1 + mx2 + cnt[u]);
        tension(f[u][3], f[e->to][2] - 1 + mx2 + cnt[u]);
        tension(mx1, f[e->to][1] - 1), tension(mx1, f[e->to][2] - 1);
        tension(mx2, f[e->to][0]);
    }

    // 交于一点
    int mx3 = 0;
    mx1 = mx2 = 0;
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) {
        int v = f[e->to][0];
        if (v > mx1) swap(mx1, v);
        if (v > mx2) swap(mx2, v);
        if (v > mx3) swap(mx3, v);
    }
    tension(f[u][3], mx1 + mx2 + cnt[u] + mx3);

    // f[u][4] 有两个完整的,不包含 u
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa)
        tension(f[u][4], f[e->to][5] + 1), tension(f[u][4], f[e->to][4]);
    // 两边继承
    mx1 = -INF, mx2 = -INF;
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) {
        tension(f[u][4], f[e->to][1] + mx1 - 1);
        tension(f[u][4], f[e->to][1] + mx2);
        tension(f[u][4], f[e->to][2] + mx1);
        tension(f[u][4], f[e->to][2] + mx2 + 1);
        tension(mx1, f[e->to][1]), tension(mx2, f[e->to][2]);
    }

    // f[u][5] 有两个完整的,包含 u
    // 封笔
    mx1 = -INF, mx2 = 0;
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) {
        tension(f[u][5], mx1 + f[e->to][0] + cnt[u] + 1);
        tension(f[u][5], f[e->to][3] + mx2 + cnt[u] + 1);
        tension(mx1, f[e->to][3]), tension(mx2, f[e->to][0]);
    }
    // 全部在 u 上
    int mx4 = 0;
    mx1 = mx2 = mx3 = 0;
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) {
        int v = f[e->to][0];
        if (v > mx1) swap(mx1, v);
        if (v > mx2) swap(mx2, v);
        if (v > mx3) swap(mx3, v);
        if (v > mx4) swap(mx4, v);
    }
    tension(f[u][5], mx1 + mx2 + mx3 + mx4 + cnt[u] + 1);

    vector<int> vec;
    vec.push_back(-1);
    for (Edge *e = first[u]; e; e = e->next) if (e->to != fa) vec.push_back(e->to);

    int sz = vec.size();
    static int pre[MAX_N], suf[MAX_N], prepre[MAX_N], sufsuf[MAX_N];
    pre[0] = prepre[0] = suf[sz] = sufsuf[sz] = 0;
    for (int i = 1; i < sz; ++i) {
        prepre[i] = max(prepre[i - 1], f[vec[i]][0] + pre[i - 1]);
        pre[i] = max(pre[i - 1], f[vec[i]][0]);
    }
    for (int i = sz - 1; i >= 1; --i) {
        sufsuf[i] = max(sufsuf[i + 1], f[vec[i]][0] + suf[i + 1]);
        suf[i] = max(suf[i + 1], f[vec[i]][0]);
    }
    for (int i = 1; i < sz; ++i) {
        int mx = max(f[vec[i]][1], f[vec[i]][2]);
        tension(f[u][5], mx + prepre[i - 1] + cnt[u]);
        tension(f[u][5], mx + sufsuf[i + 1] + cnt[u]);
        tension(f[u][5], mx + pre[i - 1] + suf[i + 1] + cnt[u]);
    }
}

int main() {
#ifdef DEBUG
    freopen("test.in", "r", stdin);
#endif
    int caseNum = readInt();
    x = readInt();

    while (caseNum--) {
        n = readInt();
        if (x >= 1) p0 = readInt() - 1, p1 = readInt() - 1;
        if (x == 2) h0 = readInt() - 1, h1 = readInt() - 1;

        pit = pool;
        for (int i = 0; i < n; ++i) first[i] = NULL;
        for (int i = 0; i < n - 1; ++i) {
            int u = readInt() - 1, v = readInt() - 1;
            first[u] = new (pit++) Edge(first[u], v);
            first[v] = new (pit++) Edge(first[v], u);
        }

        dfs(0, -1);
        dp(0, -1);

        printf("%d\n", max(f[0][4], f[0][5]));
    }

    return 0;
}

上一题