zoukankan      html  css  js  c++  java
  • CF980E The Number Games

    CF980E The Number Games

    给定一棵大小为 (n) 的树,第 (i) 个点的点权为 (2^i) ,删掉 (k) 个点及其连边,使得剩下的点组成一个连通块,且权值和最大,输出要删掉的点

    (n, kleq10^6)

    贪心,倍增,dfs序


    很容易想到一个贪心:不断删掉能被删除且权值最小的点,用堆实现

    但很明显这是错的:局部最优 ( eq) 全局最优

    因为点权为 (2^i) ,所以与其选择 (1, 2, cdots, i-1) 不如选 (i) 这一个点

    考虑这样一个 (O(n^2)) 贪心:从大往小确定不被删的点。以 (n) 为根,若当前枚举到的节点可选则将根到这个点的路径上的所有点都选掉

    可以发现,问题转化为了快速求出一个点是否可选,并标记路径

    因为最多会标记 (n-1) 条路径,所以可以暴力标记,倍增查询

    时间复杂度 (O(nlog n)) ,空间复杂度 (O(nlog n))

    原操作还有另一种实现方式:树状数组维护dfs序上每个点的距离,标记路径时修改子树内答案

    这种做法虽然略显繁琐,但是不失为一种常用的高效算法

    时间复杂度 (O(nlog n)) ,空间复杂度 (O(n))

    倍增代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e6 + 10;
    int n, k, fa[21][maxn]; bool vis[maxn];
    vector <int> e[maxn];
    
    void dfs(int u, int f) {
      fa[0][u] = f;
      for (int i = 1; i < 21; i++) {
        fa[i][u] = fa[i - 1][fa[i - 1][u]];
      }
      for (int v : e[u]) {
        if (v != f) dfs(v, u);
      }
    }
    
    int lca(int u) {
      int res = 0;
      for (int i = 20; ~i; i--) {
        if (!vis[fa[i][u]]) {
          u = fa[i][u], res |= 1 << i;
        }
      }
      return res;
    }
    
    int main() {
      scanf("%d %d", &n, &k);
      for (int i = 1, u, v; i < n; i++) {
        scanf("%d %d", &u, &v);
        e[u].push_back(v), e[v].push_back(u);
      }
      dfs(n, 0), vis[0] = 1;
      int tmp = n - k;
      for (int i = n; i; i--) {
        int t = lca(i);
        if (t < tmp) {
          int cur = i;
          while (!vis[cur]) {
            vis[cur] = 1, tmp--, cur = fa[0][cur];
          }
        }
      }
      for (int i = 1; i < n; i++) {
        if (!vis[i]) printf("%d ", i);
      }
      return 0;
    }
    

    dfs序+树状数组代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e6 + 10;
    int n, k, now, a[maxn], c[maxn], sz[maxn], fa[maxn], tid[maxn], dep[maxn]; bool vis[maxn];
    vector <int> e[maxn];
    
    void upd(int pos, int x) {
      for (; pos <= n; pos += pos & -pos) {
        c[pos] += x;
      }
    }
    
    int query(int pos) {
      int res = 0;
      for (; pos; pos &= pos - 1) {
        res += c[pos];
      }
      return res;
    }
    
    int dfs(int u, int f) {
      a[++now] = u, tid[u] = now, fa[u] = f, dep[u] = dep[f] + 1;
      for (int v : e[u]) {
        if (v != f) sz[u] += dfs(v, u);
      }
      return ++sz[u];
    }
    
    int main() {
      scanf("%d %d", &n, &k);
      for (int i = 1, u, v; i < n; i++) {
        scanf("%d %d", &u, &v);
        e[u].push_back(v), e[v].push_back(u);
      }
      dfs(n, 0), upd(1, -1);
      for (int i = 1; i <= n; i++) {
        upd(i, dep[a[i]] - dep[a[i - 1]]);
      }
      int tmp = n - k; vis[0] = 1;
      for (int i = n; i; i--) {
        if (query(tid[i]) < tmp) {
          for (int cur = i; !vis[cur]; tmp--, vis[cur] = 1, cur = fa[cur]) {
            upd(tid[cur], -1), upd(tid[cur] + sz[cur], 1);
          }
        }
      }
      for (int i = 1; i < n; i++) {
        if (!vis[i]) printf("%d ", i);
      }
      return 0;
    }
    
  • 相关阅读:
    mysql免安装使用(win7 64位系统)
    [NOIP2011]瑞士轮
    [NOIP2011]数的划分
    [洛谷2994]超级弹珠
    并查集
    [codevs1073]家族
    快速幂
    [NOI2002]银河英雄传说
    [NOIP2007]矩阵取数游戏
    [洛谷2415]集合求和
  • 原文地址:https://www.cnblogs.com/Juanzhang/p/10344604.html
Copyright © 2011-2022 走看看