码迷,mamicode.com
首页 > 其他好文 > 详细

2019 Sichuan Province Programming Contest D - Divide a Tree

时间:2021-03-09 13:33:11      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:cin   size   contest   out   ase   cst   fine   name   dep   

想清楚它不是DP而是贪心就好做了

首先, 我们称子树刚好包含所有某一颜色的点为临界点, 那我们要优先选择深度越深的临界点越好
用另一种方式来说, 在dfs的过程中, 第一次发现有一个点恰好为临界点,那么就选择这个点为分界线新增一个答案, 很容易想清楚, 选择越深的肯定更优, 让其它点的有更多分块的机会

这道题也有两种做法, 一种是找到每个颜色的临界点, 排序一遍再染色一遍即可
另一个方法是用dsu on tree, 不需要找lca, 每次找到满足条件的时候就ans++, 暴力求解即可
这里给出第一种方法的代码

#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
#define fi first
#define se second
#define mp make_pair
#define sz(v) ((int)(v).size())
#define debug(a) cout << #a << " = " << a << endl;
typedef long long ll;
typedef pair <int, int> P;
vector < int > edge[N];
const ll MOD = 1e9 + 7;
int n, m;
int a[N], Size[N], dep[N], fa[N], hs[N], tot;
int minn[N], maxn[N], dfn;
void dfs(int u, int f, int d) {
    dep[u] = d; fa[u] = f; Size[u] = 1; dfn++;
    if (minn[a[u]] > n)
        minn[a[u]] = u;
    maxn[a[u]] = u;
    for (int i = 0; i < sz(edge[u]); i++) {
        int v = edge[u][i];
        if (v == fa[u]) continue;
        dfs(v, u, d + 1);
        if (Size[v] > Size[hs[u]])  hs[u] = v;
        Size[u] += Size[v];
    }
}

int top[N];
void dfs2(int u, int T) {
    top[u] = T;
    if (!hs[u]) return ;
    dfs2(hs[u], T);
    for (int i = 0; i < sz(edge[u]); i++){
        int v = edge[u][i];
        if (hs[u] == v || v == fa[u]) continue;
        dfs2(v, v);
    }
}

inline int Lca(int u, int v) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]])  swap(u, v);
        u = fa[top[u]];
    }
    return dep[u] > dep[v] ? v : u;
}

struct node {
    int a, b, c;
    inline bool operator < (const node & o) const {
        return a > o.a;
    }
} v[N];

int col[N];
bool vis[N];
void add(int u) {
    col[a[u]] = true; vis[u] = true;
    for (int i = 0; i < sz(edge[u]); i++) {
        int v = edge[u][i];
        if (v == fa[u] || vis[v]) continue;
        add(v);
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    for (int Case = 1; Case <= T; Case++) {
        memset(minn, 0x3f, sizeof(minn));
        memset(maxn, 0xff, sizeof(maxn));
        cin >> n; tot = 0;
        for (int i = 1; i <= n; i++)    cin >> a[i];
        for (int i = 1; i < n; i++) {
            int u, v;
            cin >> u >> v;
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        dfs(1, 0, 0);
        dfs2(1, 1);
        for (int i = 1; i <= n; i++)
            if (minn[i] <= n && maxn[i] > 0) {
                int l = Lca(minn[i], maxn[i]);
                v[++tot] = {dep[l], l, i};
            }
        sort(v + 1, v + tot + 1);
        int ans = 0;
        for (int i = 1; i <= tot; i++) {
            if (col[v[i].c]) continue;
            add(v[i].b);  ans++;   
        }
        for (int i = 1; i <= n; i++)    
            vis[i] = false, col[i] = false, hs[i] = 0, edge[i].clear();
        cout << "Case " << Case << ": " << ans << endl;
    }
    return 0;
}

2019 Sichuan Province Programming Contest D - Divide a Tree

标签:cin   size   contest   out   ase   cst   fine   name   dep   

原文地址:https://www.cnblogs.com/cminus/p/14501856.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!